salvatorecervone/rolepermissionmanager

A dynamic, database-driven Role & Permission Manager for Laravel. Replaces static middleware-based permission checks with a centralized, zero-hardcoding ACL system.

Maintainers

Package info

github.com/SalvatoreCervone/rolepermissionmanager

pkg:composer/salvatorecervone/rolepermissionmanager

Transparency log

Statistics

Installs: 18

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.1.0 2026-08-20 16:03 UTC

This package is auto-updated.

Last update: 2026-08-20 16:04:03 UTC


README

Latest Version on Packagist Total Downloads Software License PHP Version Laravel Version

A modern, dynamic, database-driven Role & Permission Manager for Laravel that completely replaces static middleware annotations with a centralized, zero-hardcoding Access Control Layer (ACL).

๐Ÿ’ก The Problem with Traditional RBAC (e.g. Spatie Permission)

In traditional authorization setups, permissions are hardcoded into route definitions, controller constructors, or method calls:

// โŒ Traditional approach: Hardcoded permissions in code
Route::delete('/invoices/{id}', [InvoiceController::class, 'destroy'])
    ->middleware('permission:delete-invoices');

When business rules change (e.g. "Only Finance Supervisors can delete invoices now", or "Split permission into draft vs finalized invoices"), you must:

  1. Modify source code files across controllers and routes
  2. Commit changes to Git
  3. Create a pull request, run CI/CD, and deploy a new release to production

RolePermissionManager eliminates this bottleneck entirely.

Routes and functions are registered as Secured Resources in the database. Permissions, roles, and route access rules are mapped dynamically and cached in memory/Redis. You can change any permission rule from the built-in Web Admin Panel or database in seconds โ€” with zero code changes and zero downtime.

โœจ Features

  • ๐Ÿš€ Zero Hardcoding โ€” Define clean routes without cluttering them with permission:... middleware
  • ๐Ÿ” Route Auto-Discovery โ€” php artisan acl:sync scans your routes and registers new endpoints automatically
  • โฐ Automated Scheduler โ€” Configurable daily route synchronization to catch new endpoints
  • ๐Ÿ›ก๏ธ Single Dynamic Interceptor โ€” DynamicAclGuard middleware evaluates requests against cached ACL rules
  • โšก High Performance & Low Latency โ€” Complete cache layer (Redis/File/Memory) with automatic invalidation on Eloquent events
  • ๐ŸŽ›๏ธ AND / OR Permission Operators โ€” Choose whether a route requires all or at least one of the linked permissions
  • ๐Ÿ‘‘ Super Admin Bypass โ€” Configurable super admin role that bypasses all permission checks automatically
  • ๐Ÿ‘ค User Access Management โ€” Manage user roles and direct permissions with live autocomplete search
  • ๐Ÿ–ฅ๏ธ Built-in Web Admin Panel โ€” Modern, dark-themed dashboard for managing Roles, Permissions, Routes, and Users (no external JS dependencies)
  • ๐ŸŽจ Blade Directives โ€” @role, @haspermission, and @canRoute for views
  • ๐Ÿ”Œ Native Laravel Gate Integration โ€” Works seamlessly with $user->can() and @can
  • ๐Ÿ“ฆ Polymorphic Architecture โ€” Works with any Authenticatable model (User, Admin, Member, etc.)

๐Ÿ“‹ Requirements

  • PHP: ^8.2
  • Laravel: ^10.0 | ^11.0 | ^12.0

๐Ÿ“ฆ Installation

1. Require the package via Composer

composer require salvatorecervone/rolepermissionmanager

2. Publish Assets

You can publish all assets at once:

php artisan vendor:publish --provider="SalvatoreCervone\RolePermissionManager\RolePermissionManagerServiceProvider"

Or publish individual components using specific tags:

Component Publish Command Target Location
Config (Required) php artisan vendor:publish --tag=rolepermissionmanager-config config/rolepermissionmanager.php
Migrations (Required) php artisan vendor:publish --tag=rolepermissionmanager-migrations database/migrations/
Language Files (Optional) php artisan vendor:publish --tag=rolepermissionmanager-lang lang/vendor/acl/
Blade Views (Optional) php artisan vendor:publish --tag=rolepermissionmanager-views resources/views/vendor/acl/
Routes (Optional) php artisan vendor:publish --tag=rolepermissionmanager-routes routes/acl-web.php & routes/acl-api.php
# 1. Config file (custom table names, cache TTL, super admin, locale, etc.)
php artisan vendor:publish --tag=rolepermissionmanager-config

# 2. Database migrations (7 ACL tables)
php artisan vendor:publish --tag=rolepermissionmanager-migrations

# 3. (Optional) Language files for custom translations (EN & IT included)
php artisan vendor:publish --tag=rolepermissionmanager-lang

# 4. (Optional) Admin panel Blade views for custom branding & UI styling
php artisan vendor:publish --tag=rolepermissionmanager-views

# 5. (Optional) Custom route files for extending Web & API endpoints
php artisan vendor:publish --tag=rolepermissionmanager-routes

3. Run Database Migrations

php artisan migrate

This creates 8 tables (customizable in config):

  • acl_roles
  • acl_permissions
  • acl_secured_resources
  • acl_scanner_rules (dynamic route exclusions & inclusions)
  • acl_model_has_roles (polymorphic pivot)
  • acl_model_has_permissions (polymorphic pivot)
  • acl_role_has_permissions (pivot)
  • acl_permission_has_resources (pivot)

๐Ÿš€ Quick Start

1. Add the Trait to your User Model

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use SalvatoreCervone\RolePermissionManager\Traits\HasAcl;

class User extends Authenticatable
{
    use HasAcl;
}

2. Synchronize Application Routes

php artisan acl:sync --notify

Output:

๐Ÿ” Scanning routes...

+-------------------------------+-------+
| Action                        | Count |
+-------------------------------+-------+
| ๐Ÿ“— New routes registered      | 16    |
| ๐Ÿ“˜ Existing routes updated    | 0     |
| ๐Ÿ“™ Routes deprecated (soft)   | 0     |
| ๐Ÿ“• Routes removed (hard)      | 0     |
| โญ๏ธ  Routes skipped (excluded)  | 4     |
+-------------------------------+-------+

โœ… ACL route sync completed. Cache refreshed.

3. Write Clean Routes (No Middleware Hardcoding!)

// routes/web.php or routes/api.php
Route::get('/invoices', [InvoiceController::class, 'index'])->name('invoices.index');
Route::post('/invoices', [InvoiceController::class, 'store'])->name('invoices.store');
Route::delete('/invoices/{id}', [InvoiceController::class, 'destroy'])->name('invoices.destroy');

The global DynamicAclGuard middleware intercepts every request and verifies access dynamically against the cached ACL registry.

๐Ÿ–ฅ๏ธ Web Administration Panel

The package includes a modern, zero-dependency admin dashboard accessible at /acl-admin:

/acl-admin
โ”œโ”€โ”€ /                       โ†’ Dashboard (KPIs, statistics, recent resources, sync trigger)
โ”œโ”€โ”€ /users                  โ†’ Users list with live autocomplete search & filter by role
โ”œโ”€โ”€ /users/{id}/edit        โ†’ Assign/remove roles & direct permissions for a user
โ”œโ”€โ”€ /roles                  โ†’ Roles list with permission counts
โ”œโ”€โ”€ /roles/create           โ†’ Create role (auto-slug generator)
โ”œโ”€โ”€ /roles/{id}/edit        โ†’ Edit role & assign permissions (grouped by module)
โ”œโ”€โ”€ /permissions            โ†’ Permissions list with module filters
โ”œโ”€โ”€ /permissions/create     โ†’ Create permission with module classification
โ”œโ”€โ”€ /permissions/{id}/edit  โ†’ Edit permission & view linked roles/resources
โ”œโ”€โ”€ /resources              โ†’ Secured routes list with method/status/search filters
โ””โ”€โ”€ /resources/{id}/edit    โ†’ Configure route (Public/Protected, OR/AND operator, permissions)

๐Ÿ“– Usage & API Reference

Role Management

use SalvatoreCervone\RolePermissionManager\Models\Role;

// Create or retrieve roles
$admin = Role::findOrCreate('admin', 'Administrator');
$editor = Role::findOrCreate('editor', 'Content Editor');

// Assign roles to a user
$user->assignRole('admin');
$user->assignRole('editor', 'writer'); // Multiple roles
$user->assignRole($admin);            // By model instance

// Remove roles
$user->removeRole('editor');

// Replace all roles
$user->syncRoles('admin', 'finance');

// Role checks
$user->hasRole('admin');              // bool
$user->hasAnyRole('admin', 'editor'); // bool
$user->hasAllRoles('admin', 'editor');// bool

Permission Management

use SalvatoreCervone\RolePermissionManager\Models\Permission;

// Create permissions with module grouping
$viewUsers = Permission::findOrCreate('users.view', 'View Users', 'Users');
$deleteUsers = Permission::findOrCreate('users.delete', 'Delete Users', 'Users');

// Give permissions to roles
$admin->givePermissionTo('users.view', 'users.delete');
$admin->revokePermissionTo('users.delete');
$admin->syncPermissions('users.view');

// Direct permissions on users (independent of roles)
$user->givePermissionTo('reports.export');
$user->revokePermissionTo('reports.export');
$user->syncPermissions('reports.export', 'logs.view');

// Permission checks (inherits from roles + direct permissions)
$user->hasPermission('users.view');              // bool
$user->hasAnyPermission('users.view', 'users.edit'); // bool
$user->hasAllPermissions('users.view', 'users.delete'); // bool

// Get all permission slugs for user
$permissions = $user->getAllPermissions(); // array of slugs

Route-Level Access Verification

// Check if user has permission to access a specific route
if ($user->canAccessRoute('invoices.destroy')) {
    // Show delete button or perform action
}

Blade Directives

{{-- Check Role --}}
@role('admin')
    <a href="/admin">Admin Area</a>
@endrole

{{-- Check Permission --}}
@haspermission('users.export')
    <button>Export Users</button>
@endhaspermission

{{-- Check Route Access dynamically --}}
@canRoute('invoices.destroy')
    <button class="btn-danger">Delete Invoice</button>
@endcanRoute

{{-- Check Custom Resource Access (classes, methods, UI elements) --}}
@canResource('CorsoController@dettagliocorsi')
    <button class="btn-primary">View Course Details</button>
@endcanResource

Programmatic Resource Authorization

You can check access or enforce authorization for any route or custom resource directly in PHP code:

use SalvatoreCervone\RolePermissionManager\Services\AclRegistry;

// Check if user has access (returns boolean)
if (AclRegistry::hasAccess('CorsoController@dettagliocorsi')) {
    // Authorized
}

// Enforce authorization (throws UnauthorizedException / 403 if denied)
AclRegistry::authorize('CorsoController@dettagliocorsi');

Native Laravel Gate Integration

The package hooks into Laravel's Gate::before, allowing standard @can and $user->can() checks:

@can('users.delete')
    <button>Delete</button>
@endcan
if ($request->user()->can('invoices.export')) {
    // Authorized
}
### Menu & Navigation Tree Filtering

Filter dynamic, nested sidebar/menu structures (e.g., PrimeVue, PrimeReact, Admin menus) automatically based on the user's permissions and roles:

```php
$menu = [
    [
        'label'    => 'Uff. Valutazioni',
        'icon'     => 'pi pi-fw pi-home',
        'permessi' => ['scrivi_rapportoinformativo', 'scrivi_anagraficarelazionedirigenziale'],
        'items'    => [
            [
                'label'    => 'Organico RI',
                'url'      => '/rapportiinformativi/match',
                'permessi' => ['scrivi_rapportoinformativo'],
            ],
            [
                'label'    => 'Organico RD',
                'url'      => '/relazionidirigenziali/match',
                'permessi' => ['scrivi_anagraficarelazionedirigenziale'],
            ],
        ],
    ],
];

// Returns only the items and sub-items the user is authorized to see
$filteredMenu = auth()->user()->filterNavigation($menu);
// Or via static helper:
$filteredMenu = \SalvatoreCervone\RolePermissionManager\Services\AclRegistry::filterMenu($menu);

โš™๏ธ Configuration Reference

File: config/rolepermissionmanager.php

return [

    // Customizable database table names
    'tables' => [
        'roles'                    => 'acl_roles',
        'permissions'              => 'acl_permissions',
        'secured_resources'        => 'acl_secured_resources',
        'model_has_roles'          => 'acl_model_has_roles',
        'model_has_permissions'    => 'acl_model_has_permissions',
        'role_has_permissions'     => 'acl_role_has_permissions',
        'permission_has_resources' => 'acl_permission_has_resources',
    ],

    // Model classes
    'models' => [
        'user'              => App\Models\User::class,
        'role'              => SalvatoreCervone\RolePermissionManager\Models\Role::class,
        'permission'        => SalvatoreCervone\RolePermissionManager\Models\Permission::class,
        'secured_resource'  => SalvatoreCervone\RolePermissionManager\Models\SecuredResource::class,
    ],

    // User model and autocomplete search configuration
    'users' => [
        'table'             => 'users',
        'searchable_fields' => ['name', 'email'], // Columns searched by autocomplete
        'display_field'     => 'name',            // Primary label column
        'secondary_field'   => 'email',           // Sub-label in autocomplete
        'per_page'          => 25,
    ],

    // Super Admin role slug (bypasses all checks)
    'super_admin_role' => 'super-admin',

    // Cache settings
    'cache' => [
        'store'  => null,    // null = default store (Redis, Memcached, File)
        'ttl'    => 86400,   // 24 hours (0 = forever)
        'prefix' => 'acl_',
    ],

    // Route scanner settings
    'scanner' => [
        'excluded_prefixes' => [
            '_ignition', '_debugbar', 'sanctum', 'telescope', 'horizon', 'livewire',
        ],
        'excluded_names' => [
            'login', 'logout', 'register', 'password.request', 'password.reset',
        ],
        'default_is_public'       => false, // Secure by default
        'default_operator'        => 'OR',  // 'OR' or 'AND'
        'auto_create_permissions' => false,
    ],

    // Middleware settings
    'middleware' => [
        'register_globally'    => true,    // Applied to 'web' and 'api' groups
        'guard'                => null,    // null = default guard
        'unprotected_behavior' => 'allow', // 'allow' or 'deny'
    ],

    // Automated Scheduler
    'scheduler' => [
        'enabled' => false,
        'time'    => '06:00',
        'options' => [
            'clean'            => false,
            'auto_permissions' => false,
            'notify'           => true,
        ],
    ],

    // Web Admin Panel
    'admin_panel' => [
        'enabled'    => true,
        'prefix'     => 'acl-admin',
        'middleware' => ['web', 'auth'],
        'page_title' => 'ACL Manager',
        'per_page'   => 25,
    ],

];

๐Ÿ› ๏ธ Artisan Commands

# Basic route scan and cache rebuild
php artisan acl:sync

# Remove routes from DB that no longer exist in code
php artisan acl:sync --clean

# Automatically create permissions for all newly discovered routes
php artisan acl:sync --auto-permissions

# Detailed verbose logging of scanned routes
php artisan acl:sync --notify

# Complete sync with cleanup, permission creation, and notification
php artisan acl:sync --clean --auto-permissions --notify

๐Ÿงช Testing & Workbench Preview

Run the PHPUnit test suite:

composer test
# or
./vendor/bin/phpunit

Standalone Workbench Preview (No full app required!)

You can run and test the complete package and admin panel in an isolated SQLite workbench:

# 1. Run migrations and seed demo data
php vendor/bin/testbench migrate:fresh --seed --class="Workbench\Database\Seeders\DatabaseSeeder"

# 2. Start the local server
php vendor/bin/testbench serve --port=8080

Open http://127.0.0.1:8080 and log in with:

  • Email: admin@demo.test
  • Password: password

๐Ÿท๏ธ Versioning & Git Tags

This package follows Semantic Versioning (SemVer).

To create and publish a new release:

# 1. Tag the release
git tag -a v1.0.0 -m "Release v1.0.0: Initial release of RolePermissionManager"

# 2. Push commits and tags to GitHub
git push origin main --tags

๐Ÿ“„ License

The MIT License (MIT). Please see LICENSE for more information.