Permissions & Authorization
How to create permissions, policies, and authorization checks in LaraDashboard modules using PermissionService, Spatie Laravel Permission, and Laravel policies.
Permissions & Authorization
LaraDashboard uses Spatie Laravel Permission for role-based access control. Modules create their own permissions via migrations and enforce them through Laravel policies.
Creating Permissions
PermissionService
Use PermissionService to create CRUD permissions in migrations:
<?php
declare(strict_types=1);
use App\Services\PermissionService;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
public function up(): void
{
// Creates: contact.view, contact.create, contact.edit, contact.delete
PermissionService::createCrudPermissions('contact', 'Contact');
}
public function down(): void
{
PermissionService::deleteCrudPermissions('contact');
}
};
This creates four permissions following the {resource_snake}.{action} convention:
| Permission | Description |
|---|---|
contact.view |
View contacts list and details |
contact.create |
Create new contacts |
contact.edit |
Edit existing contacts |
contact.delete |
Delete contacts |
Custom Permissions
For non-CRUD permissions, create them directly:
use Spatie\Permission\Models\Permission;
Permission::create(['name' => 'contact.export', 'guard_name' => 'web']);
Permission::create(['name' => 'contact.import', 'guard_name' => 'web']);
Permission Naming Convention
Always use {resource_snake}.{action}:
contact.view
contact.create
contact.edit
contact.delete
deal.view
deal.create
ticket.assign
campaign.send
Creating Policies
Generate Policy
php artisan make:policy ContactPolicy --model=Contact
Or create manually in modules/{Module}/app/Policies/:
<?php
declare(strict_types=1);
namespace Modules\Crm\Policies;
use App\Models\User;
use Modules\Crm\Models\Contact;
class ContactPolicy
{
public function viewAny(User $user): bool
{
return $user->hasPermissionTo('contact.view');
}
public function view(User $user, Contact $contact): bool
{
return $user->hasPermissionTo('contact.view');
}
public function create(User $user): bool
{
return $user->hasPermissionTo('contact.create');
}
public function update(User $user, Contact $contact): bool
{
return $user->hasPermissionTo('contact.edit');
}
public function delete(User $user, Contact $contact): bool
{
return $user->hasPermissionTo('contact.delete');
}
}
Register Policy
Register the policy in your module's ServiceProvider:
use Illuminate\Support\Facades\Gate;
use Modules\Crm\Models\Contact;
use Modules\Crm\Policies\ContactPolicy;
public function boot(): void
{
Gate::policy(Contact::class, ContactPolicy::class);
}
Enforcing Authorization
In Controllers
Use $this->authorize() in every controller action:
public function index(): Renderable
{
$this->authorize('viewAny', Contact::class);
// ...
}
public function show(int $id): Renderable
{
$contact = Contact::findOrFail($id);
$this->authorize('view', $contact);
// ...
}
public function store(ContactRequest $request): RedirectResponse
{
$this->authorize('create', Contact::class);
// ...
}
public function update(ContactRequest $request, int $id): RedirectResponse
{
$contact = Contact::findOrFail($id);
$this->authorize('update', $contact);
// ...
}
public function destroy(int $id): RedirectResponse
{
$contact = Contact::findOrFail($id);
$this->authorize('delete', $contact);
// ...
}
In Blade Views
@can('create', \Modules\Crm\Models\Contact::class)
<a href="{{ route('admin.crm.contacts.create') }}" class="btn btn-primary">
Add Contact
</a>
@endcan
@can('delete', $contact)
<button class="btn btn-danger btn-sm">Delete</button>
@endcan
In Menu Service
Menu items are hidden based on permissions:
(new AdminMenuItem())->setAttributes([
'label' => __('Contacts'),
'route' => route('admin.crm.contacts.index'),
'permissions' => ['contact.view'], // Hidden if user lacks this
]);
Direct Permission Checks
// Check permission
if ($user->hasPermissionTo('contact.export')) {
// ...
}
// Check role
if ($user->hasRole('super-admin')) {
// ...
}
// Abort if unauthorized
abort_unless(auth()->user()->can('contact.edit'), 403);
Superadmin Role
The super-admin role bypasses all permission checks. This is handled automatically by Spatie's SuperAdminRole gate.
When creating new permissions, you may need to assign them to the superadmin role:
use App\Services\PermissionService;
// PermissionService::createCrudPermissions automatically assigns to super-admin
PermissionService::createCrudPermissions('contact', 'Contact');
Module Permission Flow
1. Migration creates permissions via PermissionService
2. Policy checks permissions with hasPermissionTo()
3. Policy registered in ServiceProvider via Gate::policy()
4. Controller uses $this->authorize() on every action
5. Menu items hidden via 'permissions' array
6. Blade uses @can / @cannot directives
Next Steps
- Module Development — Creating modules
- CRUD Generator — Auto-generates policies and permission migrations
- Hook System — Extend authorization with hooks