Laravel

Implementing ACL in Laravel: Role-Based Access Control

Laravel

Implementing ACL in Laravel: Role-Based Access Control

Access Control Lists (ACL) are a fundamental part of web application security, allowing you to control access to various resources based on user roles. In Laravel, implementing Role-Based Access Control (RBAC) with ACL is a powerful way to manage user permissions. In this post, we'll explore how to implement ACL in Laravel with a role-based approach, providing a full example with sample output.

Step 1: Set Up the Database

First, create a database table to store roles and permissions. For this example, we'll create two tables: roles and permissions.

CREATE TABLE roles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    description VARCHAR(255) NOT NULL
);

CREATE TABLE permissions (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    description VARCHAR(255) NOT NULL
);

Step 2: Create Models and Relationships

Next, create Eloquent models for the Role and Permission tables, along with their relationships.

php artisan make:model Role
php artisan make:model Permission

In the models, define the many-to-many relationship between roles and permissions:

// Role.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Role extends Model
{
    protected $fillable = ['name', 'description'];

    public function permissions()
    {
        return $this->belongsToMany(Permission::class);
    }
}

// Permission.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Permission extends Model
{
    protected $fillable = ['name', 'description'];

    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

Step 3: Create Middleware

Now, create a middleware that checks whether the authenticated user has the required role to access specific routes.

php artisan make:middleware RoleMiddleware

In the RoleMiddleware.php file, implement the logic to check the user's role:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class RoleMiddleware
{
    public function handle($request, Closure $next, ...$roles)
    {
        if (!Auth::check()) {
            return redirect()->route('login');
        }

        $user = Auth::user();

        foreach ($roles as $role) {
            if ($user->hasRole($role)) {
                return $next($request);
            }
        }

        return redirect()->route('unauthorized');
    }
}

Step 4: Register the Middleware

Register the RoleMiddleware in the app/Http/Kernel.php file by adding it to the $routeMiddleware array.

protected $routeMiddleware = [
    // Other middleware
    'role' => \App\Http\Middleware\RoleMiddleware::class,
];

Step 5: Define Routes and Controller

In this example, let's create two routes: one that requires the admin role and another that requires the editor role. We'll use a controller to handle the requests.

// routes/web.php
use App\Http\Controllers\HomeController;

Route::get('/', [HomeController::class, 'index']);
Route::get('/admin', [HomeController::class, 'admin'])->middleware('role:admin');
Route::get('/editor', [HomeController::class, 'editor'])->middleware('role:editor');
Route::get('/unauthorized', [HomeController::class, 'unauthorized'])->name('unauthorized');

Step 6: Implement the Controller Logic

Create a controller to handle the routes defined in Step 5 and check the user's role.

php artisan make:controller HomeController

In the HomeController.php file:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    public function index()
    {
        return view('welcome');
    }

    public function admin()
    {
        return view('admin');
    }

    public function editor()
    {
        return view('editor');
    }

    public function unauthorized()
    {
        return view('unauthorized');
    }
}

Step 7: Implement Role Check in the User Model

In the User model, define a method to check if the user has a specific role.

// User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    // Your existing User model code

    public function hasRole($role)
    {
        return $this->roles()->where('name', $role)->exists();
    }
}

Step 8: Sample Views

Create views for the admin, editor, and unauthorized routes.

<!-- resources/views/admin.blade.php -->
@extends('layouts.app')

@section('content')
    <h1>Welcome, Admin!</h1>
    <p>This is the Admin Dashboard.</p>
@endsection

<!-- resources/views/editor.blade.php -->
@extends('layouts.app')

@section('content')
    <h1>Welcome, Editor!</h1>
    <p>This is the Editor Dashboard.</p>
@endsection

<!-- resources/views/unauthorized.blade.php -->
@extends('layouts.app')

@section('content')
    <h1>Unauthorized Access</h1>
    <p>You do not have permission to access this page.</p>
@endsection

Step 9: Test the Implementation

Now, run your Laravel application and access the routes /, /admin, and /editor. You should be redirected to the /login route, as we have not implemented authentication in this example.