# Authorization Integration with Laravel's Gate and Policy system for fine-grained authorization in admin panels. ## Form Component Authorization All form components support authorization props: ```blade Publish Post ``` ### Authorization Props **`can` - Single ability:** ```blade Delete {{-- Only shown if user can delete the post --}} ``` **`cannot` - Inverse check:** ```blade {{-- Disabled if user cannot publish --}} ``` **`canAny` - Multiple abilities (OR):** ```blade Edit Post {{-- Shown if user can either edit OR update --}} ``` ## Policy Integration ### Defining Policies ```php workspace_id === $post->workspace_id; } public function create(User $user): bool { return $user->hasPermission('posts.create'); } public function update(User $user, Post $post): bool { return $user->id === $post->author_id || $user->hasRole('editor'); } public function delete(User $user, Post $post): bool { return $user->hasRole('admin') && $user->workspace_id === $post->workspace_id; } public function publish(User $user, Post $post): bool { return $user->hasPermission('posts.publish') && $post->status !== 'archived'; } } ``` ### Registering Policies ```php use Illuminate\Support\Facades\Gate; use Mod\Blog\Models\Post; use Mod\Blog\Policies\PostPolicy; // In AuthServiceProvider or module Boot class Gate::policy(Post::class, PostPolicy::class); ``` ## Action Gate Use the Action Gate system for route-level authorization: ### Defining Actions ```php middleware(['auth', ActionGateMiddleware::class]); // Protect route group Route::middleware(['auth', ActionGateMiddleware::class]) ->group(function () { Route::post('/posts', [PostController::class, 'store']); Route::post('/posts/{post}/publish', [PostController::class, 'publish']); }); ``` ### Checking Permissions ```php use Core\Bouncer\Gate\ActionGateService; $gate = app(ActionGateService::class); // Check if user can perform action if ($gate->allows('posts.create', auth()->user())) { // User has permission } // Check with additional context if ($gate->allows('posts.publish', auth()->user(), $post)) { // User can publish this specific post } // Get all user permissions $permissions = $gate->getUserPermissions(auth()->user()); ``` ## Admin Menu Authorization Restrict menu items by permission: ```php use Core\Front\Admin\Support\MenuItemBuilder; MenuItemBuilder::create('Posts') ->route('admin.posts.index') ->icon('heroicon-o-document-text') ->can('posts.view') // Only shown if user can view posts ->badge(fn () => Post::pending()->count()) ->children([ MenuItemBuilder::create('All Posts') ->route('admin.posts.index'), MenuItemBuilder::create('Create Post') ->route('admin.posts.create') ->can('posts.create'), // Nested permission check MenuItemBuilder::create('Categories') ->route('admin.categories.index') ->canAny(['categories.view', 'categories.edit']), ]); ``` ## Livewire Modal Authorization Protect Livewire modals with authorization checks: ```php authorize('update', $post); $this->post = $post; } public function save() { // Authorize action $this->authorize('update', $this->post); $this->post->save(); $this->dispatch('post-updated'); } public function publish() { // Custom authorization $this->authorize('publish', $this->post); $this->post->update(['status' => 'published']); } } ``` ## Workspace Scoping Automatic workspace isolation with policies: ```php class PostPolicy { public function viewAny(User $user): bool { // User can view posts in their workspace return true; } public function view(User $user, Post $post): bool { // Enforce workspace boundary return $user->workspace_id === $post->workspace_id; } public function update(User $user, Post $post): bool { // Workspace check + additional authorization return $user->workspace_id === $post->workspace_id && ($user->id === $post->author_id || $user->hasRole('editor')); } } ``` ## Role-Based Authorization ### Defining Roles ```php use Mod\Tenant\Models\User; // Assign role $user->assignRole('editor'); // Check role if ($user->hasRole('admin')) { // User is admin } // Check any role if ($user->hasAnyRole(['editor', 'author'])) { // User has at least one role } // Check all roles if ($user->hasAllRoles(['editor', 'reviewer'])) { // User has both roles } ``` ### Policy with Roles ```php class PostPolicy { public function update(User $user, Post $post): bool { return $user->hasRole('admin') || ($user->hasRole('editor') && $user->workspace_id === $post->workspace_id) || ($user->hasRole('author') && $user->id === $post->author_id); } public function delete(User $user, Post $post): bool { // Only admins can delete return $user->hasRole('admin'); } } ``` ## Permission-Based Authorization ### Defining Permissions ```php // Grant permission $user->givePermission('posts.create'); $user->givePermission('posts.publish'); // Check permission if ($user->hasPermission('posts.publish')) { // User can publish } // Check multiple permissions if ($user->hasAllPermissions(['posts.create', 'posts.publish'])) { // User has all permissions } // Check any permission if ($user->hasAnyPermission(['posts.edit', 'posts.delete'])) { // User has at least one permission } ``` ### Policy with Permissions ```php class PostPolicy { public function create(User $user): bool { return $user->hasPermission('posts.create'); } public function publish(User $user, Post $post): bool { return $user->hasPermission('posts.publish') && $post->status === 'draft'; } } ``` ## Conditional Rendering ### Blade Directives ```blade @can('create', App\Models\Post::class) Create Post @endcan @cannot('delete', $post)

You cannot delete this post

@endcannot @canany(['edit', 'update'], $post) Edit @endcanany ``` ### Component Visibility ```blade Publish {{-- Automatically hidden if user cannot publish --}} ``` ### Form Field Disabling ```blade {{-- Disabled if user cannot edit slug --}} ``` ## Authorization Middleware ### Global Middleware ```php // app/Http/Kernel.php protected $middlewareGroups = [ 'web' => [ // ... \Core\Bouncer\Gate\ActionGateMiddleware::class, ], ]; ``` ### Route Middleware ```php // Require authentication Route::middleware(['auth'])->group(function () { Route::get('/admin', [AdminController::class, 'index']); }); // Require specific ability Route::middleware(['can:create,App\Models\Post'])->group(function () { Route::get('/posts/create', [PostController::class, 'create']); }); ``` ## Testing Authorization ```php use Tests\TestCase; use Mod\Blog\Models\Post; use Mod\Tenant\Models\User; class AuthorizationTest extends TestCase { public function test_user_can_view_own_posts(): void { $user = User::factory()->create(); $post = Post::factory()->create(['author_id' => $user->id]); $this->assertTrue($user->can('view', $post)); } public function test_user_cannot_delete_others_posts(): void { $user = User::factory()->create(); $post = Post::factory()->create(); // Different author $this->assertFalse($user->can('delete', $post)); } public function test_admin_can_delete_any_post(): void { $admin = User::factory()->create(); $admin->assignRole('admin'); $post = Post::factory()->create(); $this->assertTrue($admin->can('delete', $post)); } public function test_workspace_isolation(): void { $user1 = User::factory()->create(['workspace_id' => 1]); $user2 = User::factory()->create(['workspace_id' => 2]); $post = Post::factory()->create(['workspace_id' => 1]); $this->assertTrue($user1->can('view', $post)); $this->assertFalse($user2->can('view', $post)); } } ``` ## Best Practices ### 1. Always Check Workspace Boundaries ```php // ✅ Good - workspace check public function view(User $user, Post $post): bool { return $user->workspace_id === $post->workspace_id; } // ❌ Bad - no workspace check public function view(User $user, Post $post): bool { return true; // Data leak! } ``` ### 2. Use Policies Over Gates ```php // ✅ Good - policy $this->authorize('update', $post); // ❌ Bad - manual check if (auth()->id() !== $post->author_id) { abort(403); } ``` ### 3. Authorize Early ```php // ✅ Good - authorize in mount public function mount(Post $post) { $this->authorize('update', $post); $this->post = $post; } // ❌ Bad - authorize in action public function save() { $this->authorize('update', $this->post); // Too late! $this->post->save(); } ``` ### 4. Use Authorization Props ```blade {{-- ✅ Good - declarative authorization --}} Delete {{-- ❌ Bad - manual check --}} @if(auth()->user()->can('delete', $post)) Delete @endif ``` ## Learn More - [Form Components →](/packages/admin/forms) - [Admin Menus →](/packages/admin/menus) - [Multi-Tenancy →](/packages/core/tenancy)