Laravel Permissions with Spatie, Practical Guide: Authorization with Roles, Installation

Video thumbnail

Spatie Laravel-Permission is an open-source package for managing user permissions based on roles used with Laravel; it is a package that is easy to use when implementing a structure as commonly handled in projects today as permissions and roles, which we will go into more detail in the next section.

In this section, we will learn in detail how to use this package in a Laravel project and, with this, be able to protect resources in a more scalable and modular way than simply indicating an enumerated type column for the role; remember that we previously saw how to employ Test Driven Development (TDD) in Laravel.

Roles and permissions

In typical systems where it is required to protect application resources, roles and permissions are usually used to manage controlled access to each of the resources; roles and permissions are a mechanism to control and restrict access to different parts of a web application.

Permissions are specific actions that a user can perform in the application, for example, "publish a new article" or "delete a comment." With Spatie laravel-permission, you can associate roles and permissions to users and verify if a user has access to a specific action in the application based on their roles and permissions.

Roles are a way to group permissions, for example, you could have an "administrator" role that has permissions for all actions in your application, while a "user" role would only have permissions for limited actions.

To understand what was mentioned through an example, in the context of a web application, roles can be, for example, "administrator", "editor" and "reader". Each role has a different set of permissions that determines what actions it can perform.

For posts in an administrator role:

  • Create post.
  • Update post.
  • Delete post.
  • Detail/List post.

For categories in an administrator role:

  • Create category.
  • Update category.
  • Delete category.
  • Detail/List category.

For posts in an editor role:

  • Create post.
  • Update post (only their own).
  • Delete post (only their own).
  • Detail/List post.

For categories in an editor role:

  • Create category.
  • Update category (only their own).
  • Delete category (only their own).
  • Detail/List category.

For posts in a reader role:

  • Detail/List post.

For categories in a reader role:

  • Detail/List category.

You can get more information at:

https://spatie.be/docs/laravel-permission/v5/introduction

https://laravel-news.com/laravel-gates-policies-guards-explained

Installation

The installation of this package is the typical process in which we execute a composer command indicating the package we want to install:

$ composer require spatie/laravel-permission

Register the provider:

config/app.php

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

Publish the migration and the configuration file config/permission.php to be able to customize it:

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

Run the migration:

$ php artisan migrate

And we will have an output like the following:

2023_04_16_125650_create_permission_tables ................... 796ms DONE

With the previous command, several tables are created, you can inspect the permissions migration (***_create_permission_tables) and you will see that it consists of several tables:

  • roles
  • permissions
  • model_has_permissions
  • model_has_roles
  • role_has_permissions

Expert tip: "Remember that spatie/laravel-permission doesn't just create tables, it creates a polymorphic Many-to-Many relationship. If you ever need to check permissions manually, use $user->getAllPermissions() to see the total union of direct permissions and those inherited by roles."

To be able to use permissions from the user entity, we register the roles trait:

use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
   use *** HasRoles;
   ***
}

Methods for assigning: Permissions to roles, roles to permissions and users, permissions and roles

Video thumbnail

In this section, we are going to learn how to assign permissions to each corresponding entity, which can be of 3 types:

  1. Assign permissions to roles; this is the most common case since, in the end, roles are nothing more than a grouping of permissions, that is, a role can have from zero to N permissions.
  2. Assign roles to users; a user can have from zero to N roles, which internally contain the permissions, and with this, control access to the application modules.
  3. Assign permissions to users; although it is not the ideal approach, we can also assign permissions directly to users.

Let's look at these cases in detail:

Roles to permissions and/or users

With a permission or user, we can manage roles in the following way:

  • removeRole() Removes a role from the user/permission.
  • assignRole() Assigns a role to the user/permission.
  • syncRoles() To synchronize roles, you must provide an array with the roles:
    • The role or roles that are not established as arguments of this function but are assigned to the user/permission will be removed.
    • The role or roles that are established as arguments of this function and are assigned to the user/permission are kept, and those that were not assigned to the user/permission will be added.

Permissions to roles (or roles to permissions)

As we mentioned before, roles contain the permissions that define access for users; if we check the roles model:

vendor\spatie\laravel-permission\src\Models\Role.php

public function permissions(): BelongsToMany{}

And through the trait of:

Spatie\Permission\Traits\HasPermissions;

We will see that we have access to a series of methods equivalent to those presented above:

  • revokePermissionTo()
  • syncPermissions()
  • givePermissionTo()

In a Policy, it looks like this:

// In your PostPolicy.php
public function update(User $user, Post $post) {
   // 1. Spatie: Does it have the general permission?
   if ($user->can('edit all posts')) return true;
   // 2. Policy: Is it the original author?
   return $user->id === $post->user_id;
}
FeatureSpatie Laravel-PermissionLaravel Policies (Native)
FocusGlobal and reusable roles and permissions.Model-specific business logic.
ComplexityIdeal for systems with many roles.Ideal for rules based on ownership (e.g., post->user_id).
StorageData persisted in database.Code based on classes and methods.
ReusabilityVery high (dynamic assignment).Low (logic coupled to a model).
Ideal for...Admin panels, SaaS.Resource authorization by owner/author.

Policies vs Spatie in Laravel (Roles and Permissions) when to use?

Video thumbnail

I wanted to make a quick clarification on when to use Spatie and when to use policies in Laravel. I assume you already have basic knowledge of the subject, but I will explain it quickly.

Roles and Permissions Systems - Spatie

Spatie is a system that allows managing roles and permissions in a simple way. For example, you can assign one or more roles to a user, and each role can have specific permissions to perform certain actions within the application.

Practical example

Suppose we have a blog-type application with a basic CRUD (create, list, edit, delete). We can define roles such as:

  • Editor: can edit articles and view lists, but not create or delete.
  • Administrator: has access to all CRUD operations.

In practice, Spatie facilitates this logic in a reusable way:

$user->assignRole('admin');
$user->givePermissionTo('edit articles');
if ($user->can('edit articles')) {
   // Access granted
}

Here, basically, permissions translate into conditionals that control access to resources. The advantage is that you can easily reuse these permissions and assign them to both users and roles.

What are Policies in Laravel

Policies are a more homemade and personalized system that allows controlling access to resources based on the business logic of your application. They are ideal when you do not need a complex system of roles and permissions, such as the one offered by Spatie.

Practical example

 

In my application, I have a system of books and courses. Every time a user buys a book, a record is generated in the database:

$filePayment = FilePayment::where('user_id', $user->id)
   ->where('file_paymentable_id', $bookSection->book->id)
   ->where('file_paymentable_type', Book::class)
   ->first();
if ($filePayment == null)
   return [202, "You have not purchased the book"];

This is a good candidate for defining a policy, which would be responsible for validating if the user has access to the book's content. This way, repeating conditionals in multiple places of the application is avoided.

Policies are linked to business logic and models. While Spatie is more generic and oriented towards large systems, policies allow for finer control over specific resources and logical steps within the application.

Basically, everything translates into conditionals. The functions we see in Spatie act as intermediaries for a conditional, but in the background, we are always evaluating whether or not a user has a permission:

$user->assignRole('admin');
$user->givePermissionTo('edit articles');
if ($user->can('edit articles')) {
   // Access granted
}

With this, we have simple reuse of permissions: you can assign a permission to several users and verify it uniformly.

This system is very similar to what we find in Django, although in Laravel with Spatie it integrates easily with other components, such as policies. For example, you can:

Assign a role to a user.

Give permissions to that role automatically or independently.

In this way, Spatie takes care of managing permissions for you, and from there you only apply conditionals to verify if a user can perform an action, for example:

can('edit articles')

You simply ask if the user has that permission, and that's it.

What are policies

Now, what is a policy?
Policies are similar to Spatie, but a bit more generic and focused on the business logic of your application. They are normally used when you don't need a complex system of roles and permissions.

In other words, while Spatie handles permissions and roles globally and reusably, policies allow you to define more specific rules directly linked to the models or resources of your application.

In the next practical example, we will see how to use policies to control access to certain resources without depending on a full roles and permissions system.

Integration between Spatie and Policies

In more complex applications, you can combine Spatie and policies:

  • Spatie: defines general roles and permissions.
  • Policies: implements specific rules based on business logic.

For example, you could have permissions to access books and, within a policy, validate that the user has actually purchased that book before allowing them to view the content.

Summary

  • Spatie: generic roles and permissions system, useful for large applications and with many users and roles.
  • Policies: more specific control linked to business logic and models, ideal for validating access to resources granularly.

In summary, policies focus on the internal logic of your application, while Spatie provides a general scheme of reusable permissions. Both can be combined according to the needs of your project.

The next step we will see is how to integrate CKEditor in Laravel.

Roles or Policies? Learn how to manage permissions in Laravel with Spatie and implement granular authorizations with policies. Step-by-step guide.

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español