Skip to main content

Overview

LaraCMS uses the Spatie Permission package for role-based access control (RBAC). The system provides flexible permission management with roles, permissions, and a special Super Admin role that bypasses all permission checks.

User Model Integration

The User model integrates Spatie Permission at app/Models/User.php:10:
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable implements MustVerifyEmail, HasMedia
{
    use HasFactory, HasRoles, Notifiable, InteractsWithMedia;
}
The HasRoles trait provides methods for assigning and checking roles and permissions.

Default Roles

LaraCMS includes six pre-defined roles, seeded during migration at database/migrations/2025_03_30_145142_create_permission_tables.php:180:

Super Admin

Role::create(['name' => 'Super Admin']);
// no perms – full access handled by Gate::before
Super Admin has unrestricted access to all features and bypasses all permission checks via a Gate policy. This role should only be assigned to trusted system administrators.

Admin

$role = Role::create(['name' => 'admin']);
$role->givePermissionTo([
    'manage.users',
    'access.admin.panel',
    'view.unpublished.posts',
    'publish.posts',
    'unpublish.posts',
    'create.events',
    'edit.events',
    'delete.events',
    'publish.events',
    'unpublish.events',
    'view.unpublished.events',
    'create.images',
    'edit.images',
    'delete.images',
    'manage.albums',
    'create.videos',
    'edit.videos',
    'delete.videos',
    'publish.videos',
    'unpublish.videos',
    'view.unpublished.videos',
    'manage.org.members',
    'assign.org.roles',
    'manage.org.settings',
]);
Admins have extensive permissions for content management, user management, and organizational settings.

Editor

$role = Role::create(['name' => 'editor']);
$role->givePermissionTo([
    'create.posts',
    'edit.posts',
    'delete.posts',
    'publish.posts',
    'unpublish.posts',
    'view.unpublished.posts',
    'access.admin.panel',
]);
Editors can manage and publish content but cannot manage users or system settings.

Writer

$role = Role::create(['name' => 'writer']);
$role->givePermissionTo([
    'create.posts',
    'edit.posts',
    'delete.posts',
]);
Writers can create and edit posts but cannot publish them.

Org Member

$role = Role::create(['name' => 'org member']);
$role->givePermissionTo([
    'view.member.dashboard',
]);
Organization members have access to member-specific features.

User

$role = Role::create(['name' => 'user']);
// no perms – just a base role for logged-in accounts
The ‘user’ role is automatically assigned to all new registrations. It serves as the base role for authenticated accounts without special permissions.

Permission System

LaraCMS defines granular permissions organized by feature area.

Posts Permissions

Permission::create(['name' => 'create.posts']);
Permission::create(['name' => 'edit.posts']);
Permission::create(['name' => 'delete.posts']);
Permission::create(['name' => 'publish.posts']);
Permission::create(['name' => 'unpublish.posts']);
Permission::create(['name' => 'view.unpublished.posts']);
Permission::create(['name' => 'create.images']);
Permission::create(['name' => 'edit.images']);
Permission::create(['name' => 'delete.images']);
Permission::create(['name' => 'manage.albums']);

Videos Permissions

Permission::create(['name' => 'create.videos']);
Permission::create(['name' => 'edit.videos']);
Permission::create(['name' => 'delete.videos']);
Permission::create(['name' => 'publish.videos']);
Permission::create(['name' => 'unpublish.videos']);
Permission::create(['name' => 'view.unpublished.videos']);

Events Permissions

Permission::create(['name' => 'create.events']);
Permission::create(['name' => 'edit.events']);
Permission::create(['name' => 'delete.events']);
Permission::create(['name' => 'publish.events']);
Permission::create(['name' => 'unpublish.events']);
Permission::create(['name' => 'view.unpublished.events']);

Organization Permissions

Permission::create(['name' => 'view.member.dashboard']);
Permission::create(['name' => 'manage.org.members']);
Permission::create(['name' => 'assign.org.roles']);
Permission::create(['name' => 'manage.org.settings']);

Admin Permissions

Permission::create(['name' => 'manage.users']);
Permission::create(['name' => 'access.admin.panel']);
Permissions follow a consistent naming pattern: action.resource (e.g., create.posts, manage.users). This makes permissions easy to understand and maintain.

Super Admin Gate Policy

Super Admin role bypasses all permission checks via app/Providers/AppServiceProvider.php:28:
public function boot(): void
{
    // Implicitly grant "Super Admin" role all permissions
    // This works in the app by using gate-related functions like auth()->user->can() and @can()
    Gate::before(function ($user, $ability) {
        return $user->hasRole('Super Admin') ? true : null;
    });
}
This Gate::before callback intercepts all permission checks and returns true for Super Admins before checking individual permissions.
Important: The Super Admin bypass only works with Laravel’s Gate methods like auth()->user()->can() and Blade’s @can() directive. Direct permission checks like hasPermissionTo() will not be bypassed.

Route Protection

Routes are protected using the permission middleware.

Admin Panel Access

Route::prefix('admin')
    ->name('admin.')
    ->middleware('auth', 'verified', 'permission:access.admin.panel')
    ->group(function () {
        // Admin routes
    });

Subscriber Area

Route::get('/subscribers', function () {
    return view('subscribers');
})->middleware('auth', 'verified', 'permission:access.subscriber.area');

Resource Routes

Route::prefix('admin')->middleware('auth', 'verified', 'permission:access.admin.panel')->group(function () {
    // User Management
    Route::resource('users', UserController::class);
    
    // Roles & Permissions Management
    Route::resource('roles', RoleController::class);
});

Checking Permissions in Code

Using Gate Facade

use Illuminate\Support\Facades\Gate;

if (Gate::allows('create.posts')) {
    // User can create posts
}

if (Gate::denies('delete.posts')) {
    // User cannot delete posts
}

Using User Model Methods

// Check permission
if ($user->can('edit.posts')) {
    // User has permission
}

// Check role
if ($user->hasRole('admin')) {
    // User is an admin
}

// Check any role
if ($user->hasAnyRole(['admin', 'editor'])) {
    // User has at least one of these roles
}

// Check all roles
if ($user->hasAllRoles(['admin', 'editor'])) {
    // User has all these roles
}

// Direct permission check (bypasses Super Admin gate)
if ($user->hasPermissionTo('create.posts')) {
    // User has this specific permission
}

In Blade Templates

@can('create.posts')
    <a href="{{ route('admin.blog.create') }}">Create Post</a>
@endcan

@role('admin')
    <a href="{{ route('admin.users.index') }}">Manage Users</a>
@endrole

@hasrole('admin|editor')
    <a href="{{ route('admin.dashboard') }}">Admin Dashboard</a>
@endhasrole

In Livewire Components

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class CreatePost extends Component
{
    use AuthorizesRequests;

    public function save()
    {
        $this->authorize('create.posts');
        
        // Create post logic
    }
}

Assigning Roles & Permissions

During Registration

New users automatically get the ‘user’ role at app/Livewire/Auth/Register.php:46:
event(new Registered(($user = User::create($validated))));

// Assign the 'user' role after the user is created
$user->assignRole('user');

Assigning Roles Programmatically

// Assign single role
$user->assignRole('editor');

// Assign multiple roles
$user->assignRole(['writer', 'editor']);

// Sync roles (removes all existing roles and assigns new ones)
$user->syncRoles(['editor']);

// Remove role
$user->removeRole('writer');

Assigning Permissions Directly

// Give permission to user
$user->givePermissionTo('edit.posts');

// Give multiple permissions
$user->givePermissionTo(['edit.posts', 'delete.posts']);

// Revoke permission
$user->revokePermissionTo('delete.posts');

Managing Role Permissions

$role = Role::findByName('editor');

// Add permission to role
$role->givePermissionTo('publish.posts');

// Add multiple permissions
$role->givePermissionTo(['publish.posts', 'unpublish.posts']);

// Sync permissions
$role->syncPermissions(['create.posts', 'edit.posts', 'publish.posts']);

// Remove permission
$role->revokePermissionTo('delete.posts');

Permission Caching

Spatie Permission caches permissions for 24 hours for performance at config/permission.php:186:
'cache' => [
    'expiration_time' => \DateInterval::createFromDateString('24 hours'),
    'key' => 'spatie.permission.cache',
    'store' => 'default',
],

Clearing Permission Cache

After making changes to roles or permissions, clear the cache:
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
The migration automatically clears the cache after seeding:
// Refresh permission cache
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
Always clear the permission cache after modifying roles or permissions, otherwise changes may not take effect immediately.

Database Tables

Spatie Permission creates five database tables:

Core Tables

  • permissions - Stores all available permissions
  • roles - Stores all roles
  • role_has_permissions - Pivot table linking roles to permissions
  • model_has_permissions - Pivot table for direct user permissions
  • model_has_roles - Pivot table linking users to roles

Table Configuration

Table names are configurable at config/permission.php:31:
'table_names' => [
    'roles' => 'roles',
    'permissions' => 'permissions',
    'model_has_permissions' => 'model_has_permissions',
    'model_has_roles' => 'model_has_roles',
    'role_has_permissions' => 'role_has_permissions',
],

Configuration Options

Permission Check Registration

'register_permission_check_method' => true,
Registers permission checking methods on Laravel’s Gate.

Teams Feature

'teams' => false,
LaraCMS does not enable the teams feature by default.

Exception Messages

'display_permission_in_exception' => false,
'display_role_in_exception' => false,
Permission names are not included in exception messages by default to prevent information leakage. Enable these options in development for better debugging.

Wildcard Permissions

'enable_wildcard_permission' => false,
Wildcard permissions (e.g., posts.*) are disabled by default.

User Management Routes

Admins can manage users and roles through dedicated routes:
// User Management
Route::resource('users', UserController::class);

// Roles & Permissions Management
Route::resource('roles', RoleController::class);
These routes provide full CRUD operations for managing users, roles, and permissions through the admin panel.

Best Practices

Security Recommendations:
  • Assign Super Admin role only to trusted administrators
  • Use the principle of least privilege - give users only the permissions they need
  • Regularly audit user roles and permissions
  • Clear permission cache after making changes
  • Use role-based permissions rather than direct user permissions for easier management
  • Protect sensitive routes with multiple middleware layers (auth, verified, permission)
Permission Naming Convention:
  • Use dot notation: action.resource
  • Keep actions lowercase: create, edit, delete, manage, view
  • Keep resource names plural where appropriate: posts, users, events
  • Be specific: view.unpublished.posts is clearer than view.posts.unpublished

Next Steps

Authentication

Learn about user authentication and registration flows

User Profiles

Managing user profiles, avatars, and media uploads

Build docs developers (and LLMs) love