ssntpl / laravel-acl
Laravel ACL package.
Installs: 9
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/ssntpl/laravel-acl
Requires
- php: ^8.0
- laravel/framework: ^10.0|^11.0|^12.0
This package is auto-updated.
Last update: 2025-11-26 04:37:39 UTC
README
Laravel ACL is a framework-agnostic access control layer for Laravel 10/11/12 projects. It provides global and resource-scoped roles, explicit ALLOW/DENY permissions, permission implication trees, and cache-friendly lookups that plug straight into your existing Eloquent models through a reusable trait.
Features
- Global or resource-scoped role assignments stored via polymorphic pivots.
- Permission inheritance (parent → child) with cached graph traversal.
- Explicit ALLOW/DENY effects per role-permission pair layered on top of implied permissions.
- Role assignment expirations and helper trait (
HasRoles) for Eloquent subjects. - Cache invalidation hooks plus an
acl:cache-resetartisan command. - First-class artisan tooling for creating permissions, roles, and assignments.
- HTTP middleware for checking global roles or permissions in routes/controllers.
Requirements
- PHP ^8.0
- Laravel framework ^10.0 | ^11.0 | ^12.0
Installation
composer require ssntpl/laravel-acl
- (Optional) Publish the config and migrations:
php artisan vendor:publish --tag=acl-config php artisan vendor:publish --tag=acl-migrations
- Run the migrations:
php artisan migrate
Configuration (config/acl.php)
return [ 'cache_ttl' => env('ACL_CACHE_TTL', 86400), ];
cache_ttl– lifetime (seconds) for cached permission trees and role lookups.
Database schema
Publishing the migration adds:
| Table | Key columns | Purpose |
|---|---|---|
acl_roles |
name, resource_type, description |
Defines each role; resource_type is null for global roles. |
acl_permissions |
name, resource_type |
Defines permissions; names are unique. |
acl_role_permissions |
role_id, permission_id, effect |
Pivot with ALLOW/DENY effect per permission. |
acl_role_assignments |
subject_type, subject_id, role_id, optional resource_*, expires_at |
Links a subject (e.g., user) to a role, optionally scoped to a resource, with expiration support. |
acl_permissions_implications |
parent_permission_id, child_permission_id |
Models permission implication trees (grant parent ⇒ grant child). |
Usage
1. Add HasRoles to the authenticatable model
namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Ssntpl\LaravelAcl\Traits\HasRoles; class User extends Authenticatable { use HasRoles; }
2. Create permissions
Interactive artisan flow:
php artisan acl:create-permission articles.publish "App\\Models\\Project" --implied="articles.read|articles.list"
Programmatically:
use Ssntpl\LaravelAcl\Models\Permission; $publish = Permission::create(['name' => 'articles.publish']); $read = Permission::firstOrCreate(['name' => 'articles.read']); $publish->children()->attach($read); // Publish implies read
3. Create roles and attach permissions
php artisan acl:create-role admin "App\\Models\\Project" "articles.read|articles.publish|comments.moderate"
In PHP you can use either syncPermissions() (implicit ALLOW) or syncPermissionsWithEffect():
use Ssntpl\LaravelAcl\Models\Role; $role = Role::firstOrCreate([ 'name' => 'admin', 'resource_type' => App\Models\Project::class, ]); $role->syncPermissionsWithEffect([ $publish->id => 'ALLOW', $read->id => 'ALLOW', ]);
4. Assign roles
Using the trait:
$user = User::find(1); $project = Project::find(42); $user->assignRole('admin', $project); // scoped role $user->assignRole('super-admin', null, now()->addMonth()); // global role with expiration
Using the command (handy for ops/support teams):
php artisan acl:assign-role admin App\\Models\\User:1 App\\Models\\Project:42 --expires-at="2025-12-31 23:59:59"
5. Check roles & permissions
if ($user->hasRole('admin', $project)) { // subject is an admin of this project } $role = $user->getRole(); // global role assignment (resource = null) if ($role && $role->can('articles.publish')) { // allowed via direct or implied permission (and not explicitly denied) }
removeRole($resource = null) deletes an assignment, and calling getRole($resource) returns the underlying Role model instance if one exists and is not expired.
HTTP middleware
Register the middleware aliases in app/Http/Kernel.php:
protected $middlewareAliases = [ 'check_global_role' => \Ssntpl\LaravelAcl\Http\Middleware\CheckGlobalRole::class, 'check_global_permission' => \Ssntpl\LaravelAcl\Http\Middleware\CheckGlobalPermission::class, ];
Usage:
Route::get('/admin', fn () => 'ok')->middleware('check_global_role:admin|manager'); Route::post('/articles', fn () => 'ok')->middleware('check_global_permission:articles.publish|articles.create');
Both middleware assume global assignments (resource is null) when evaluating the authenticated subject.
The authenticated guard’s model must use the
HasRolestrait (or at least expose compatiblehasRole/getRolemethods) because the middleware works directly withAuth::user()without re-querying the database.
Caching & invalidation
Role permissions and implied permission trees are cached per record using the configured TTL. Cache invalidation happens automatically when:
- Permissions are created, updated, deleted, or their implication edges change.
- Roles are updated or their pivot records change.
- The
acl:cache-resetcommand is executed.
Run a full reset manually with:
php artisan acl:cache-reset
Artisan command reference
| Command | Description |
|---|---|
acl:create-permission |
Create/update a permission and optionally attach implied permissions (supports interactive prompts). |
acl:create-role |
Create a role and assign permissions in one step. |
acl:assign-role |
Attach a role to a subject/resource pair with optional expiration. |
acl:cache-reset |
Flush cached permissions and role lookups. |
Support & contributing
PRs are welcome. Please include reproduction steps or tests when reporting/patching bugs.