PHPackages                             artisanpack-ui/rbac - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. artisanpack-ui/rbac

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

artisanpack-ui/rbac
===================

Role-based access control for Laravel — roles with hierarchy, permissions, Blade directives, middleware, and Gate integration.

1.0.0(3w ago)0272MITPHPPHP ^8.2CI passing

Since May 19Pushed 3w agoCompare

[ Source](https://github.com/ArtisanPack-UI/rbac)[ Packagist](https://packagist.org/packages/artisanpack-ui/rbac)[ RSS](/packages/artisanpack-ui-rbac/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (10)Versions (2)Used By (2)

ArtisanPack UI — RBAC
=====================

[](#artisanpack-ui--rbac)

Role-based access control for Laravel: roles with hierarchy, permissions, Blade directives, middleware, and Gate integration.

This package is **standalone** — it does not depend on `artisanpack-ui/security` and can be used by any Laravel application or package (e.g. `cms-framework`) that needs roles and permissions without pulling in the full security suite.

Features
--------

[](#features)

- Eloquent `Role` and `Permission` models with parent/child role hierarchy
- `HasRoles` and `HasPermissions` traits for the `User` model
- Recursive permission resolution through the role hierarchy, with per-user caching
- Route middleware (`permission:slug,slug`) for permission-gated routes
- `@role` and `@permission` Blade directives
- Gate integration (`$user->can('slug')`, `Gate::allows('slug')`, `@can('slug')`) backed by RBAC permissions
- Artisan commands for CRUD over roles and role assignments
- Configurable model bindings so consumers can extend `Role` / `Permission` with their own subclasses
- Eloquent observer events on the `rbac.*` channel for downstream auditing

Installation
------------

[](#installation)

```
composer require artisanpack-ui/rbac
```

Run the package migrations:

```
php artisan migrate
```

(Optional) Publish the config file to override model bindings, table names, or cache settings:

```
php artisan vendor:publish --tag=rbac-config
```

Quick start
-----------

[](#quick-start)

Add the traits to your `User` model:

```
use ArtisanPackUI\Rbac\Concerns\HasPermissions;
use ArtisanPackUI\Rbac\Concerns\HasRoles;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasPermissions;
    use HasRoles;
}
```

Create a role + permission and assign them:

```
use ArtisanPackUI\Rbac\Models\Permission;
use ArtisanPackUI\Rbac\Models\Role;

$editor = Role::create(['name' => 'editor']);
$publish = Permission::create(['name' => 'posts.publish']);

$editor->permissions()->attach($publish);
$user->assignRole('editor');

$user->hasRole('editor');             // true
$user->hasPermissionTo('posts.publish'); // true
$user->can('posts.publish');             // true (via Gate integration)
```

Models
------

[](#models)

### Role

[](#role)

`ArtisanPackUI\Rbac\Models\Role`

FieldTypeNotes`name`stringunique`slug`stringunique; auto-derived from `name` via `Str::slug()` if not provided`description`string|null`parent_id`int|nullforeign key to `roles.id`; `set null` on parent deleteRelationships:

- `permissions()` → `BelongsToMany Permission`
- `users()` → `BelongsToMany` against `auth.providers.users.model`
- `parent()` → `BelongsTo Role`
- `children()` → `HasMany Role`

Slug helpers: `Role::findBySlug('editor')`, `Role::whereSlug('editor')->first()`. `hasRole('editor')` and `assignRole('editor')` match against either `name` or `slug`.

Deleting a role detaches its permissions and nulls `parent_id` on its children.

### Permission

[](#permission)

`ArtisanPackUI\Rbac\Models\Permission`

FieldTypeNotes`name`stringunique`slug`stringunique; auto-derived from `name` via `Str::slug()` if not provided`description`string|nullRelationships:

- `roles()` → `BelongsToMany Role`

Slug helpers: `Permission::findBySlug('pages.publish')`, `Permission::whereSlug('pages.publish')->first()`. `hasPermissionTo('pages.publish')` and the Gate integration both match against either `name` or `slug`.

Deleting a permission detaches it from all roles.

Traits
------

[](#traits)

### `HasRoles`

[](#hasroles)

Adds the `roles()` relationship plus assignment helpers.

```
$user->roles;                          // collection of Role
$user->hasRole('admin');               // by name, Role instance, or Collection
$user->assignRole('admin');            // idempotent; no-op for unknown role names
$user->removeRole('admin');
```

`assignRole` and `removeRole` dispatch the `rbac.user.role_assigned` and `rbac.user.role_removed` events with the user and role.

### `HasPermissions`

[](#haspermissions)

Resolves the user's effective permission set by walking the role hierarchy. Pair with `HasRoles`.

```
$user->hasPermissionTo('posts.publish');
$user->hasPermission('posts.publish');     // alias of hasPermissionTo
$user->flushPermissionCache();             // call after manual relationship mutations
```

The trait caches the resolved permission collection per user. The cache TTL is configurable via `artisanpack.rbac.cache.user_permissions_ttl`.

Middleware
----------

[](#middleware)

The service provider aliases `permission` to `ArtisanPackUI\Rbac\Http\Middleware\CheckPermission`. Apply it to routes that require one or more permissions — the user must hold at least one to proceed.

```
Route::get('/posts/{post}/publish', PublishController::class)
    ->middleware('permission:posts.publish');

Route::get('/admin', AdminController::class)
    ->middleware('permission:admin.dashboard,admin.metrics');
```

Unauthenticated requests abort with `401`, authorized requests return `200`, and authenticated requests without any of the listed permissions abort with `403`.

Blade directives
----------------

[](#blade-directives)

```
@role('admin')
    Admin dashboard
@endrole

@permission('posts.publish')
    Publish
@endpermission
```

Both directives render nothing for unauthenticated users.

`@permission` resolves through the user's effective permission set, which means it composes with role hierarchy. Use Laravel's built-in `@can` directive when you want the same check to flow through the Gate integration:

```
@can('posts.publish')
    Publish
@endcan
```

Gate integration
----------------

[](#gate-integration)

The service provider registers a `Gate::before` hook that resolves any ability matching a known permission slug through `hasPermissionTo()`. Standard Laravel authorization patterns work out of the box:

```
$user->can('posts.publish');           // true|false
Gate::allows('posts.publish');         // true|false
Gate::denies('posts.publish');         // true|false
```

Abilities that don't correspond to an RBAC permission slug fall through to the normal Gate/Policy lookup, so policies you've defined elsewhere continue to work.

The list of known permission slugs is cached for `artisanpack.rbac.cache.permission_names_ttl` seconds. The cache is invalidated automatically when permissions are created, updated, or deleted.

Artisan commands
----------------

[](#artisan-commands)

```
php artisan role:create {name} [--slug=...] [--description=...]
php artisan permission:create {name} [--slug=...] [--description=...]
php artisan user:assign-role {user} {role}
php artisan user:revoke-role {user} {role}
```

`{user}` accepts a numeric ID or any value that matches one of the configured `user_lookup_fields` (defaults to `email`). The assign/revoke commands are idempotent.

Configuration reference
-----------------------

[](#configuration-reference)

Published to `config/artisanpack/rbac.php`.

```
return [
    'models' => [
        'role'       => ArtisanPackUI\Rbac\Models\Role::class,
        'permission' => ArtisanPackUI\Rbac\Models\Permission::class,
    ],

    'tables' => [
        'role_user'       => 'role_user',
        'permission_role' => 'permission_role',
        'users'           => 'users',
    ],

    'foreign_keys' => [
        'user' => 'user_id',
    ],

    'user_lookup_fields' => ['email'],

    'cache' => [
        'user_permissions_ttl' => 60,
        'permission_names_ttl' => 3600,
        'tag'                  => 'rbac',
    ],
];
```

Extending the base models
-------------------------

[](#extending-the-base-models)

Other packages can extend `Role` and `Permission` and rebind them through config — useful when a downstream package needs extra relationships or scopes without forking this package. The `cms-framework` package uses this pattern for its Users module.

```
namespace App\Models;

use ArtisanPackUI\Rbac\Models\Role as BaseRole;

class Role extends BaseRole
{
    protected $table = 'roles';

    public function policies(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(Policy::class);
    }
}
```

```
// config/artisanpack/rbac.php
'models' => [
    'role'       => App\Models\Role::class,
    'permission' => ArtisanPackUI\Rbac\Models\Permission::class,
],
```

The Artisan commands, observers, traits, and migrations all read this config, so the substituted class is used everywhere.

Events
------

[](#events)

The service provider attaches observers that dispatch:

- `rbac.role.created`, `rbac.role.updated`, `rbac.role.deleted`
- `rbac.permission.created`, `rbac.permission.updated`, `rbac.permission.deleted`

Pivot mutations are dispatched directly from the trait helpers since Laravel does not fire pivot events:

- `rbac.user.role_assigned`, `rbac.user.role_removed`

Listen for these in any application or sibling package — `security-analytics` uses them for audit logging.

Testing
-------

[](#testing)

```
composer test
```

Tests run against an in-memory SQLite database via Orchestra Testbench. The package's own test suite covers role / permission models, traits, middleware, Artisan commands, Blade directives, Gate integration, observers, and the configurable model-binding pattern.

Sibling packages
----------------

[](#sibling-packages)

PackageScope[`artisanpack-ui/security-full`](https://github.com/ArtisanPack-UI/security-full)Meta-package — pulls in the full security suite (all six packages below) in a single require[`artisanpack-ui/security`](https://github.com/ArtisanPack-UI/security)Core: input sanitization, output escaping, KSES, CSP, security headers[`artisanpack-ui/security-auth`](https://github.com/ArtisanPack-UI/security-auth)2FA, password complexity, account lockout, sessions[`artisanpack-ui/security-advanced-auth`](https://github.com/ArtisanPack-UI/security-advanced-auth)WebAuthn, SSO, social login, biometric, device fingerprinting[`artisanpack-ui/secure-uploads`](https://github.com/ArtisanPack-UI/secure-uploads)File validation, malware scanning, signed-URL serving[`artisanpack-ui/security-analytics`](https://github.com/ArtisanPack-UI/security-analytics)Event logging, anomaly detection, SIEM, dashboards[`artisanpack-ui/compliance`](https://github.com/ArtisanPack-UI/compliance)GDPR / CCPA / LGPD consent, data subject rights, DPIA, retention, monitoringContributing
------------

[](#contributing)

As an open source project, this package is open to contributions from anyone. Please [read through the contributing guidelines](CONTRIBUTING.md) to learn more about how you can contribute.

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance96

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

21d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ba2a2c40c9a93470595cd10701d2291434f3a7db61862d9700a9e69e31608c6c?d=identicon)[JacobMartellaWebDesign](/maintainers/JacobMartellaWebDesign)

---

Top Contributors

[![ViewFromTheBox](https://avatars.githubusercontent.com/u/8247489?v=4)](https://github.com/ViewFromTheBox "ViewFromTheBox (32 commits)")

---

Tags

laravelsecurityauthorizationrolespermissionsrbacaccess-control

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/artisanpack-ui-rbac/health.svg)

```
[![Health](https://phpackages.com/badges/artisanpack-ui-rbac/health.svg)](https://phpackages.com/packages/artisanpack-ui-rbac)
```

###  Alternatives

[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k3.5M115](/packages/bezhansalleh-filament-shield)[hasinhayder/tyro

Tyro - The ultimate Authentication, Authorization, and Role &amp; Privilege Management solution for Laravel 12 &amp; 13

6753.6k5](/packages/hasinhayder-tyro)[hosseinhezami/laravel-permission-manager

Advanced permission manager for Laravel.

393.3k](/packages/hosseinhezami-laravel-permission-manager)[erag/laravel-role-permission

A simple and easy-to-install role and permission management package for Laravel, supporting versions 10.x and 11.x

404.2k](/packages/erag-laravel-role-permission)[phpzen/laravel-rbac

Role based access control for Laravel 5

423.2k](/packages/phpzen-laravel-rbac)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
