directorytree/authorization
Native role & permission management for Laravel. Install via Composer, run migrations, add the Authorizable trait to your User model, then check roles/permissions, use caching, gate registration, and middleware. Includes customizable migrations/models and tests.
## Getting Started
### First Steps
1. **Installation**: Add the package via Composer:
```bash
composer require directorytree/authorization
Run migrations to create the required tables:
php artisan migrate
User Model Setup: Attach the Authorizable trait to your User model:
use DirectoryTree\Authorization\Traits\Authorizable;
class User extends Authenticatable
{
use Authorizable;
}
First Use Case: Create a permission and assign it to a role:
// Create a permission
$createPost = Permission::create(['name' => 'posts.create', 'label' => 'Create Post']);
// Create a role
$editor = Role::create(['name' => 'editor', 'label' => 'Editor']);
// Assign permission to role
$editor->permissions()->save($createPost);
// Assign role to user
$user->roles()->save($editor);
Check Permissions: Use Laravel's built-in can() method:
if (auth()->user()->can('posts.create')) {
// User has permission
}
Role-Based Access Control (RBAC):
admin, editor, user) and assign permissions to them.roles() relationship.$adminRole = Role::create(['name' => 'admin']);
$user->roles()->attach($adminRole);
Permission Management:
grant()/revoke() methods for flexibility:
$user->grant(['posts.create', 'posts.edit']); // Grant multiple permissions
$user->revoke('posts.delete'); // Revoke a single permission
Middleware Integration:
permission or role middleware:
Route::get('/admin', function () {
// ...
})->middleware('permission:posts.create');
Route::get('/admin', function () {
// ...
})->middleware('permission:posts.create,posts.edit');
Caching:
Authorization::disablePermissionCache(); // Disable caching
Authorization::cacheKey('custom.key');
Authorization::cacheExpiresIn(now()->addHours(1));
Gate Integration:
Authorization::disableGateRegistration()).public function update(Request $request, Post $post)
{
$this->authorize('posts.update');
// ...
}
Bulk Operations:
$role->grantOnly(['posts.create', 'posts.edit']); // Only these permissions remain
$user->revokeAll(); // Remove all permissions
Seeding: Use database seeds to predefine roles/permissions:
public function run()
{
$admin = Role::create(['name' => 'admin']);
$createPost = Permission::create(['name' => 'posts.create']);
$admin->permissions()->save($createPost);
}
APIs: Use the same methods in API controllers:
public function store(Request $request)
{
$this->authorize('posts.create');
// ...
}
Views: Leverage @can directives:
@can('posts.create')
<button>Create Post</button>
@endcan
Testing: Register permissions in setUp():
protected function setUp(): void
{
parent::setUp();
app(PermissionRegistrar::class)->register();
}
Cache Invalidation:
php artisan cache:clear
ClearsCachedPermissions trait on models to auto-flush:
class Permission extends Model
{
use ClearsCachedPermissions;
}
Middleware Order:
permission middleware after auth ensures unauthenticated users bypass permission checks.Route::get('/profile', function () {
// ...
})->middleware(['auth', 'permission:profile.view']);
Permission Naming:
posts.create) for consistency with Laravel Gates.Model Customization:
// Missing trait on custom Role model:
class Role extends Model
{
// Forgot: use ManagesPermissions;
}
ManagesPermissions, HasUsers, etc.).Gate Registration:
disableGateRegistration() may break existing Gate-based checks:
Gate::allows('posts.create'); // Fails if not registered
Testing Quirks:
PermissionRegistrar isn’t initialized in setUp().TestCase:
protected function setUp(): void
{
parent::setUp();
app(PermissionRegistrar::class)->register();
}
Check Cache:
Cache::get('authorization.permissions');
Log Permissions:
if (auth()->user()->can('posts.create')) {
\Log::info('Permission granted: posts.create');
}
Verify Relationships:
$user->roles; // Check assigned roles
$role->permissions; // Check role permissions
Middleware Debugging:
dd() to inspect middleware parameters:
Route::get('/debug', function () {
dd(request()->route()->middleware());
})->middleware('permission:posts.create');
Custom Permission Logic:
hasPermission() in your User model:
public function hasPermission($permission)
{
// Custom logic here
return parent::hasPermission($permission);
}
Dynamic Permissions:
posts.{id}.edit):
$user->grant("posts.$postId.edit");
Policy Integration:
public function update(Request $request, Post $post)
{
$this->authorize('posts.update', $post);
// ...
}
Event Listeners:
Permission::created(function ($permission) {
// Clear cache or log changes
});
API Tokens:
if (auth()->check() && auth()->user()->can('api.access')) {
// Allow token-based access
}
Default Cache Driver:
.env cache driver is configured (e.g., CACHE_DRIVER=file for testing).Model Binding:
class User extends Model
{
use Authorizable; // Required for core functionality
}
Laravel Version Compatibility:
Migration Conflicts:
roles/permissions tables:
php artisan vendor:publish --tag=authorization-migrations --force
Permission Registrar:
app(PermissionRegistrar::class)->register() in test setups.
```markdown
### Daily Developer Workflow Example
1. **Morning Setup**:
- Add a new permission for a feature:
```php
Permission::create(['name' => 'reports.generate', 'label' => 'Generate Reports']);
```
- Assign to the `analyst` role:
```php
$analystRole->grant('reports.generate');
```
2. **Route Protection**:
-
How can I help you explore Laravel packages today?