Laravel ACL

Laravel Permission

Multiple user roles and permission controls are required features for the admin panel.

Getting started

Install Composer Packages

Now, we will install spatie package for ACL.

composer require spatie/laravel-permission

Now open config/app.php file and add the following provider.

'providers' => [
// ...
    Spatie\Permission\PermissionServiceProvider::class,
];

Now we need to publish the spatie roles and persion using the following commands.

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Spatie create three migrations you can see after running the migration command:

php artisan migrate

Basic Usage

First, add the Spatie\Permission\Traits\HasRoles trait to your User model(s):

use Illuminate\Foundation\Auth\User as Authenticatable;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use HasRoles;

    // ...
}

This package allows for users to be associated with permissions and roles. Every role is associated with multiple permissions. A Role and a Permission are regular Eloquent models. They require a name and can be created like this:

use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

$role = Role::create(['name' => 'writer']);
$permission = Permission::create(['name' => 'edit articles']);

A permission can be assigned to a role using 1 of these methods:

$role->givePermissionTo($permission);
$permission->assignRole($role);

Multiple permissions can be synced to a role using 1 of these methods:

$role->syncPermissions($permissions);
$permission->syncRoles($roles);

A permission can be removed from a role using 1 of these methods:

$role->revokePermissionTo($permission);
$permission->removeRole($role);

If you're using multiple guards the guard_name attribute needs to be set as well. Read about it in the using multiple guards section of the readme.

The HasRoles trait adds Eloquent relationships to your models, which can be accessed directly or used as a base query:

// get a list of all permissions directly assigned to the user
$permissionNames = $user->getPermissionNames(); // collection of name strings
$permissions = $user->permissions; // collection of permission objects

// get all permissions for the user, either directly, or from roles, or from both
$permissions = $user->getDirectPermissions();
$permissions = $user->getPermissionsViaRoles();
$permissions = $user->getAllPermissions();

// get the names of the user's roles
$roles = $user->getRoleNames(); // Returns a collection

The HasRoles trait also adds a role scope to your models to scope the query to certain roles or permissions:

$users = User::role('writer')->get(); // Returns only users with the role 'writer'

The role scope can accept a string, a \Spatie\Permission\Models\Role object or an \Illuminate\Support\Collection object.

The same trait also adds a scope to only get users that have a certain permission.

$users = User::permission('edit articles')->get(); // Returns only users with the permission 'edit articles' (inherited or directly)

The scope can accept a string, a \Spatie\Permission\Models\Permission object or an \Illuminate\Support\Collection object.

ELOQUENT

Since Role and Permission models are extended from Eloquent models, basic Eloquent calls can be used as well:

$all_users_with_all_their_roles = User::with('roles')->get();
$all_users_with_all_direct_permissions = User::with('permissions')->get();
$all_roles_in_database = Role::all()->pluck('name');
$users_without_any_roles = User::doesntHave('roles')->get();
$all_roles_except_a_and_b = Role::whereNotIn('name', ['role A', 'role B'])->get();
Using a middleware
Default Middleware

For checking against a single permission (see Best Practices) using can, you can use the built-in Laravel middleware provided by \Illuminate\Auth\Middleware\Authorize::class like this

Route::group(['middleware' => ['can:publish articles']], function () {
    //
});
Package Middleware

This package comes with RoleMiddleware, PermissionMiddleware and RoleOrPermissionMiddleware middleware. You can add them inside your app/Http/Kernel.php file.

protected $routeMiddleware = [
// ...
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];

Then you can protect your routes using middleware rules:

Route::group(['middleware' => ['role:super-admin']], function () {
    //
});

Route::group(['middleware' => ['permission:publish articles']], function () {
    //
});

Route::group(['middleware' => ['role:super-admin','permission:publish articles']], function () {
    //
});

Route::group(['middleware' => ['role_or_permission:super-admin|edit articles']], function () {
    //
});

Route::group(['middleware' => ['role_or_permission:publish articles']], function () {
    //
});

Alternatively, you can separate multiple roles or permission with a | (pipe) character:

Route::group(['middleware' => ['role:super-admin|writer']], function () {
    //
});

Route::group(['middleware' => ['permission:publish articles|edit articles']], function () {
    //
});

Route::group(['middleware' => ['role_or_permission:super-admin|edit articles']], function () {
    //
});

You can protect your controllers similarly, by setting desired middleware in the constructor:

public function __construct()
{
    $this->middleware(['role:super-admin','permission:publish articles|edit articles']);
}
public function __construct()
{
    $this->middleware(['role_or_permission:super-admin|edit articles']);
}
Defining a Super-Admin

We strongly recommend that a Super-Admin be handled by setting a global Gate::before or Gate::after rule which checks for the desired role.

Then you can implement the best-practice of primarily using permission-based controls (@can and $user->can, etc) throughout your app, without always having to check for "is this a super-admin" everywhere. Best not to use role-checking (ie: hasRole) when you have Super Admin features like this.

Gate::before

If you want a "Super Admin" role to respond true to all permissions, without needing to assign all those permissions to a role, you can use Laravel's Gate::before() method. For example:

use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->registerPolicies();

        // 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;
        });
    }
}
Gate::after

Alternatively you might want to move the Super Admin check to the Gate::after phase instead, particularly if your Super Admin shouldn't be allowed to do things your app doesn't want "anyone" to do, such as writing more than 1 review, or bypassing unsubscribe rules, etc.

// somewhere in a service provider

Gate::after(function ($user, $ability) {
    return $user->hasRole('Super Admin'); // note this returns boolean
});
Blade directives
Permissions

This package doesn't add any permission-specific Blade directives. Instead, use Laravel's native @can directive to check if a user has a certain permission.

@can('edit articles')
//
@endcan

or

@if(auth()->user()->can('edit articles') && $some_other_condition)
//
@endif

You can use @can, @cannot, @canany, and @guest to test for permission-related access.

Roles

As discussed in the Best Practices section of the docs, it is strongly recommended to always use permission directives, instead of role directives.

Additionally, if your reason for testing against Roles is for a Super-Admin, see the Defining A Super-Admin section of the docs.

If you actually need to test for Roles, this package offers some Blade directives to verify whether the currently logged in user has all or any of a given list of roles.

Optionally you can pass in the guard that the check will be performed on as a second argument.

BLADE AND ROLES

Check for a specific role:

@role('writer')
I am a writer!
@else
I am not a writer...
@endrole

is the same as

@hasrole('writer')
    I am a writer!
@else
    I am not a writer...
@endhasrole

Check for any role in a list:

@hasanyrole($collectionOfRoles)
    I have one or more of these roles!
@else
    I have none of these roles...
@endhasanyrole
// or
@hasanyrole('writer|admin')
    I am either a writer or an admin or both!
@else
    I have none of these roles...
@endhasanyrole

Check for all roles:

@hasallroles($collectionOfRoles)
    I have all of these roles!
@else
    I do not have all of these roles...
@endhasallroles
// or
@hasallroles('writer|admin')
    I am both a writer and an admin!
@else
    I do not have all of these roles...
@endhasallroles

Alternatively, @unlessrole gives the reverse for checking a singular role, like this:

@unlessrole('does not have this role')
    I do not have the role
@else
    I do have the role
@endunlessrole

You can also determine if a user has exactly all of a given list of roles:

@hasexactroles('writer|admin')
    I am both a writer and an admin and nothing else!
@else
    I do not have all of these roles or have more other roles...
@endhasexactroles
© Velzon.
Design & Develop by Themesbrand