curly-deni / laravel-permission-controller
A Laravel package that observes model events and automatically enforces create, update, and delete permissions based on policies.
Fund package maintenance!
curly-deni
Requires
- php: ^8.0
- illuminate/auth: ^10.0||^11.0||^12.0
- illuminate/contracts: ^10.0||^11.0||^12.0
- illuminate/support: ^10.0||^11.0||^12.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.14
README
Permission Controller is a lightweight Laravel package that automatically enforces create
, update
, delete
, and optionally read
permissions at the model level based on your policy methods. It streamlines permission handling and improves application security with minimal setup.
Features
- 🛡️ Automatic permission checks for
create
,update
,delete
, and optionalread
actions. - ⚡ Seamless integration with Laravel’s native authorization system (policies).
- ⚙️ Highly configurable — control enabled actions and exception behavior per action.
- 🧩 Simple trait-based integration for Eloquent models.
- 📚 Clean, modular, and extendable architecture.
Installation
Install the package via Composer:
composer require curly-deni/laravel-permission-controller
Publish the configuration file:
php artisan vendor:publish --tag="permission-controller-config"
Configuration
The published configuration file config/permission-controller.php
looks like this:
return [ 'read_scope' => \Aesis\PermissionController\Scopes\ReadScope::class, 'observer' => \Aesis\PermissionController\Observers\ActionObserver::class, 'create' => [ 'enable' => true, 'exception' => \Aesis\PermissionController\Exceptions\CreateModelForbidden::class, 'throw_exception' => false, ], 'update' => [ 'enable' => true, 'exception' => \Aesis\PermissionController\Exceptions\UpdateModelForbidden::class, 'throw_exception' => false, ], 'delete' => [ 'enable' => true, 'exception' => \Aesis\PermissionController\Exceptions\DeleteModelForbidden::class, 'throw_exception' => false, ], 'read' => [ 'enable' => false, 'exception' => \Aesis\PermissionController\Exceptions\ReadModelForbidden::class, 'throw_exception' => false, ], ];
Configuration options:
read_scope
: The scope class applied to model queries to restrict access based onread
permissions.observer
: The observer class that enforces permission checks on model events (creating
,updating
,deleting
).
Per-action settings (create
, update
, delete
, read
):
enable
: Enable or disable permission enforcement for the specific action.exception
: Exception class to throw when permission is denied (ifthrow_exception
istrue
).throw_exception
: Iftrue
, the package will throw an exception; otherwise, the action will simply not proceed.
If exceptions are enabled, these exception classes are used:
Action | Exception Class |
---|---|
Create | Aesis\PermissionController\Exceptions\CreateModelForbidden |
Update | Aesis\PermissionController\Exceptions\UpdateModelForbidden |
Delete | Aesis\PermissionController\Exceptions\DeleteModelForbidden |
Read | Aesis\PermissionController\Exceptions\ReadModelForbidden |
Tip: You can override the exception classes to provide custom messages, error codes, or even logging.
Usage
1. Add the Trait to Your Models
Include the HasPermissionController
trait in any Eloquent model you want to protect:
use Aesis\PermissionController\Traits\HasPermissionController; class Post extends Model { use HasPermissionController; }
2. Define Policy Methods
You must implement policy methods only for the actions that are enabled in the configuration:
- If
'create'
is enabled, implement acreate(User $user)
method. - If
'update'
is enabled, implement anupdate(User $user, Model $model)
method. - If
'delete'
is enabled, implement adelete(User $user, Model $model)
method. - If
'read'
is enabled, implement aread(User $user)
method (without passing the model instance).
Example for a PostPolicy
:
class PostPolicy { public function create(User $user) { return $user->hasPermission('create-posts'); } public function update(User $user, Post $post) { return $user->id === $post->user_id; } public function delete(User $user, Post $post) { return $user->id === $post->user_id; } public function read(User $user) { return $user->hasPermission('read-posts'); } }
Important:
Theread
method only accepts theUser
object — no model instance is passed.
Credits
License
The MIT License (MIT). Please see License File for more information.