KodeBLOG

Kode Blog - Inspiring And Empowering Developers.

Home About Us Courses Categories Blog Contact Us

Laravel 5 Users and Roles Management

Introduction

In the previous tutorial, we implemented the authentication system for our admin panel. That provides some level of security for our application but we want to go further than that in this tutorial.

We don’t want all authenticated users to be able to perform all tasks in the system. We want to limit them according to their access rights. We will be able to do that using roles and permissions.

By the end of this tutorial, you would have known how to implemented authorization in Laravel based on roles and permissions The following screen shots shows you what you will build by the end of this tutorial.

Laravel User Roles

Topics to be covered

In this tutorial, we will cover the following topics

  • Tutorial prerequisites
  • Roles and permissions
  • Larashop Admin Roles and Permissions
  • Laravel Entrust – Role Based Permissions
    • Installation & configuration
    • Routes Middleware
    • Migrations
  • Larashop Admin Panel User Roles and Permissions
  • Complete Tutorial Code

Tutorial prerequisites

This tutorial assumes;

  • You understand the basics of Laravel 5. If you do then I recommend you start with these Laravel 5 Tutorial series.
  • You have PHP, MySQL and a web server i.e. Apache up and running
  • Laravel Homestead – this is optional but provides a great development environment. If you are new to Laravel homestead then I recommend you read the series on Laravel Homestead
  • You have composer installed
  • You have a text editor or IDE that supports PHP.
  • You have a modern web browser that supports HTML5
  • Git – this is optional but highly recommended. This tutorial has a repository on GitHub, with Git, you will be able to clone starter code for each tutorial.

Roles and permissions

Let’s take a quick look at what roles and permissions are.

Roles

A role represents a group of tasks that a user that is assigned the role is allowed to perform. For example, the system administrator can be regarded as the owner of the system and as such, is permitted to perform all the tasks in the system. He/she can create users, delete and edit products etc.

Another role can be that of a shopkeeper. The shopkeeper’s role may be limited to tasks such as creating records but not allowed to edit or delete the records. The shopkeeper may also not be allowed to create users in the system and assign roles to them.

Why roles?

Assigning permissions to individual users is possible but can become time consuming when you have a lot of users whose job description requires them to do the same tasks. In such cases, it is easier to create a role that has all the permissions and all you have to do is assign the role to the user.

Permissions

Permission grants authorization to a role to perform a specific task. For example, you can define a permission called edit. Any role that is assigned the permission edit will be able to edit data in the system. You can also have permissions such as create and delete.

Larashop Admin Roles and Permissions

The following table shows the roles and permissions that we will implement.

Roles table

S/N Role Permissions
1 Administrator Create, edit, delete, users
2 Shopkeeper Create, edit

Permissions table

S/N Permission Description
1 create Grants authorization to create any record except users
2 edit Grants authorization to edit any record except users
3 delete Grants authorization to delete any record except users
4 users Grants permission to display, create, edit, assign user roles, and delete users

Laravel Entrust – Role Based Permissions

Entrust is a third-party package that allows us to implement role based permissions in Laravel. The official GitHub repository for entrust is https://github.com/Zizaco/entrust

Installation and configuration

We will use composer to install the package

Run the following command

composer require "zizaco/entrust"

After composer has successfully added the package, you will need to modify /config/app.php and add Entrust service provider and alias

add the following line to the list of service providers

Zizaco\Entrust\EntrustServiceProvider::class,

Add the following line to the aliases array

'Entrust'   => Zizaco\Entrust\EntrustFacade::class,

Save the changes

We now need to publish the configuration file

Run the following artisan command

php artisan vendor:publish

the above command will publish the configuration file /config/entrust.php.

The configuration file contains settings for the roles and permissions table. It also defines the models that entrust requires.

open the file /config/entrust.php

By default, models are placed in the /app directory but we are using /app/Models in this project

Update the roles model to the following

'role' => 'Larashop\Models\Role',

Update the permissions model to the following

'permission' => 'Larashop\Models\Permission',

Route Middleware

We will use middleware to check if the user is authorized to access the resource before granting permission

Open /app/Http/Kernel.php

Add the following to routeMiddleware

'role' => \Zizaco\Entrust\Middleware\EntrustRole::class,
'permission' => \Zizaco\Entrust\Middleware\EntrustPermission::class,
'ability' => \Zizaco\Entrust\Middleware\EntrustAbility::class,

Roles and permissions database migrations

Before we generate the database migrations, let’s first look at the entity relationship diagram (ERD) for Entrust

Laravel Entrust ERD

In a nutshell, users are related to roles and roles are related to permissions.

The nature of the relationship between roles and permissions is many to many hence the intermediate table permission_role.

The nature of the relationship between roles and users is many to many hence the intermediate table role_user.

Entrust comes with its own artisan command that generates a single migration file that contains schemas for all the required tables.

Run the following command

php artisan entrust:migration

A migration file <timestamp>_entrust_setup_tables.php will be added to /database/migrations directory. As of this writing, the generated migration file has an error in it. We will need to fix it before you migrate the migration.

When creating the relationship between role_user and user, locate the following line

$table->foreign('user_id')->references('id')->on('')

Replace it with the following

$table->foreign('user_id')->references('id')->on('users')

Run the following command to migrate the migration file.

php artisan migrate

the above command will create the necessary tables for you.

Let’s now create the models for roles and permissions table.

Run the following artisan commands

php artisan make:model Models\Role
php artisan make:model Models\Permission

Open /app/Models/Role.php

Replace the code with the following

<?php

namespace Larashop\Models;

use Zizaco\Entrust\EntrustRole;

class Role extends EntrustRole
{
    //
}

HERE,

  • namespace Larashop\Models; defines the model namespace
  • use Zizaco\Entrust\EntrustRole; imports the Entrust role model that implements the EntrustRoleInterface and uses the EntrustRoleTrait trait. The trait is responsible for creating relationships and performing other tasks
  • class Role extends EntrustRole{} the model Role extends the class EntrustRole and not eloquent directly

Open /app/Models/Permission.php

Replace the code with the following

<?php

namespace Larashop\Models;

use Zizaco\Entrust\EntrustPermission;

class Permission extends EntrustPermission
{
    //
}

HERE,

  • namespace Larashop\Models; defines the model namespace
  • use Zizaco\Entrust\EntrustPermission; imports the entrust permission model that implements the EntrustPermissionInterface and uses the EntrustPermissionTrait trait. The trait is responsible for creating relationships and perform other tasks.

We are almost out of the woods. We need to modify the User model so that it can use EntrustUserTrait trait. We have a simple challenge.

We are using soft deletes on all our models. The SoftDeletes trait has a restore method and EntrustUserTrait also has a restore method. Using both traits on our User model will create a conflict.

For us to resolve the trait method name, we will use the traits on the User model as follows

use SoftDeletes, EntrustUserTrait {
    SoftDeletes::restore insteadof EntrustUserTrait;
    EntrustUserTrait::restore insteadof SoftDeletes;
}

HERE,

  • The above usage resolves the trait method name conflict.

If you are not using soft deletes, then you can use the traits the normal way.

use EntrustUserTrait;

The complete User model code is as follows

<?php

namespace Larashop\Models;

use Illuminate\Support\Facades\Config;
use Illuminate\Notifications\Notifiable;
use Zizaco\Entrust\Traits\EntrustUserTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Larashop\Notifications\LarashopAdminResetPassword as ResetPasswordNotification;

class User extends Authenticatable
{
    use Notifiable;

    use SoftDeletes, EntrustUserTrait {
        SoftDeletes::restore insteadof EntrustUserTrait;
        EntrustUserTrait::restore insteadof SoftDeletes;
    }

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
    'name', 
    'email', 
    'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
    'password', 
    'remember_token',
    ];

    public function getAvatarUrl()
    {
        return "https://www.gravatar.com/avatar/" . md5($this->email) . "?d=mm";
    }
}

Roles and permissions seeds

We want to keep things very simple so we will just use a seeder to create the roles and permissions as described in the above tables

Run the following command artisan commands

php artisan make:seeder PermissionsTableSeeder 
php artisan make:seeder RolesTableSeeder

Open /database/seeds/PermissionsTableSeeder.php

Replace the code with the following

<?php

use Larashop\Models\Permission;
use Illuminate\Database\Seeder;

class PermissionsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $permissions = [
            [
                'name' => 'create',
                'display_name' => 'Create Record',
                'description' => 'Allow user to create a new DB record'
            ],
            [
                'name' => 'edit',
                'display_name' => 'Edit Record',
                'description' => 'Allow user to edit an existing DB record'
            ],
            [
                'name' => 'delete',
                'display_name' => 'Delete Record',
                'description' => 'Allow user to delete an existing DB record'
            ],
            [
                'name' => 'users',
                'display_name' => 'Manage Users',
                'description' => 'Allow user to manage system users'
            ]
        ];

        foreach ($permissions as $key => $value) {
            Permission::create($value);
        }
    }
}

Open /database/seeds/RolesTableSeeder.php with the following

<?php

use Larashop\Models\Role;
use Illuminate\Database\Seeder;

class RolesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $roles = [
            [
                'name' => 'admin',
                'display_name' => 'Administrator',
                'description' => 'User has access to all system functionality'
            ],
            [
                'name' => 'shop-keeper',
                'display_name' => 'Shop Keeper',
                'description' => 'User can create create data in the system'
            ]
        ];

        foreach ($roles as $key => $value) {
            Role::create($value);
        }
    }
}

Run the following commands to seed the records

php artisan db:seed --class=PermissionsTableSeeder
php artisan db:seed --class=RolesTableSeeder

Larashop Admin Panel User Roles and Permissions

We have almost covered most of the implementation. We now need to use the middleware on the respective routes to check for necessary permissions before granting access.

For most routes, we would do this in the /routes/web.php routes configuration. But we are working with resource routes so we will inject the middleware using the controller constructor method.

Let’s use /app/Http/Controllers/Admin/BrandsController.php as an example

Add the following constructor method

public function __construct()
{
    $this->middleware('permission:create', ['only' => ['create', 'store']]);    
    $this->middleware('permission:edit', ['only' => ['edit', 'update']]);   
    $this->middleware('permission:delete', ['only' => ['show', 'delete']]);
}

HERE,

  • $this->middleware('permission:create', ['only' => ['create', 'store']]); adds create permission middleware to the routes create and store only. If the user tries to access either create or store routes, the middleware will check to see if the user has the required authorization. If the user does not have access, a 403 exception will be thrown. We will look at how to handle that in the next section.
  • $this->middleware('permission:edit', ['only' => ['edit', 'update']]); adds edit permission middleware to routes edit and update. Only users with edit and update permissions will be able to access these routes
  • $this->middleware('permission:delete', ['only' => ['show', 'delete']]); add the delete permission middleware to routes show and delete. Only users with show and delete permissions will be able to access these routes.

The same constructor method code should be applied to the rest of the controllers except for UsersController

Add the following constructor method to UsersController

public function __construct()
{
    $this->middleware('permission:users');
}

HERE,

  • $this->middleware('permission:users'); adds the users permission middlware to all of the routes defined in the UsersController.

Laravel Authorization Exception Handling

Laravel throws a 403 exception when a user without the required access rights tries to access the route.

Create a new view 403.blade.php in /resources/views/errors

Add the following code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <!-- Meta, title, CSS, favicons, etc. -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Permission Denied</title>

    <!-- Bootstrap -->
    <link href="{{asset('admin/css/bootstrap.min.css')}}" rel="stylesheet">
    <!-- Font Awesome -->
    <link href="{{asset('admin/css/font-awesome.min.css')}}" rel="stylesheet">
    <!-- NProgress -->
    <link href="{{asset('admin/css/nprogress.css')}}" rel="stylesheet">

    <!-- Custom Theme Style -->
    <link href="{{asset('admin/css/custom.min.css')}}" rel="stylesheet">
  </head>

  <body class="nav-md">
    <div class="container body">
      <div class="main_container">
        <!-- page content -->
        <div class="col-md-12">
          <div class="col-middle">
            <div class="text-center text-center">
              <h1 class="error-number">403</h1>
              <h2>Access denied</h2>
              <p>You do not have permission to access this resource.
              </p>
            </div>
          </div>
        </div>
        <!-- /page content -->
      </div>
    </div>

    <!-- jQuery -->
    <script src="{{asset('admin/js/jquery.min.js')}}"></script>
    <!-- Bootstrap -->
    <script src="{{asset('admin/js/bootstrap.min.js')}}"></script>
    <!-- FastClick -->
    <script src="{{asset('admin/js/fastclick.js')}}"></script>
    <!-- NProgress -->
    <script src="{{asset('admin/js/nprogress.js')}}"></script>

    <!-- Custom Theme Scripts -->
    <script src="{{asset('admin/js/custom.min.js')}}"></script>
  </body>
</html>

How it works?

If a user with a role that does not have permission tries to access a particular route, Laravel throws a 403 exception and looks in the /resources/views/errors directory to see if a view that matches the return status code is found. If it is found, then that view is returned.

Complete Tutorial Code

The complete code for this tutorial can be cloned from the following branch

git clone -b 07_user_roles_and_permissions https://github.com/KodeBlog/Laradmin.git laradmin

Run the following command to browser to the root of the project

cd laradmin

We will now use composer to install dependencies.

composer install

When the installation is completed, run the following artisan command

php artisan serve

Summary

In this tutorial, we have looked at how we can use Laravel Entrust to implement user roles and permissions. A role defines a number of permissions that a user is authorized to perform. A perform is simply an authorization to perform a specific task in the system.

What’s next?

The next tutorial is on Laravel localization. We want our system to be able to the system in their own language.

Kode Blog Tutorials is dedicated to bring you update to date, high quality free tutorials. You can support us by using the social media buttons to like and share the tutorial and subscribing to our newsletter. Please use the comments section below to give us feedback.

Each week, a new Laravel tutorial is added to our collection. Subscribe to our newsletter, like our Facebook fan page or follow us on Twitter to get free updates when the collection is updated.