How to implement an access level system in Laravel (Part 1)

Hossein Molavi
5 min readDec 31, 2020

--

This is my very first article on medium and I’m so excited
hope to enjoy it

Let’s assume that we want to give limited access to admins of your system,
for example, some of the admins only can see the users but some of them can only delete the users

Let’s jump right into it

create a fresh laravel project

laravel new blog

Set your database connection in .env file and run the migrations

php artisan migrate && php artisan db:seed

In this project, we have User, Article models (as simple as possible)

php artisan make:model Article -a

Now create a simple CRUD for Article

<?php

namespace
App\Http\Controllers;

use App\Models\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
/**
* Display a listing of the resource.
*
*
@return \Illuminate\Http\Response|string
*/
public function
index()
{
return "get article list";
}

/**
* Show the form for creating a new resource.
*
*
@return \Illuminate\Http\Response|string
*/
public function
create()
{
return "create new article";
}
/**
* Display the specified resource.
*
*
@param \App\Models\Article $article
*
@return \Illuminate\Http\Response|string
*/
public function
show(Article $article)
{
return "shw one article";
}

/**
* Update the specified resource in storage.
*
*
@param \Illuminate\Http\Request $request
*
@param \App\Models\Article $article
*
@return \Illuminate\Http\Response|string
*/
public function
update(Request $request, Article $article)
{
return "edit article";
}

/**
* Remove the specified resource from storage.
*
*
@param \App\Models\Article $article
*
@return \Illuminate\Http\Response|string
*/
public function
destroy(Article $article)
{
return "delete article";
}
}

Now we must implement an Authentication system
in this project I used JWT

To install JWT package just run command:

composer require tymon/jwt-auth

And you need to do some changes in your User Model

<?php

namespace
App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;

// Rest omitted for brevity

/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
*
@return mixed
*/
public function
getJWTIdentifier()
{
return $this->getKey();
}

/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
*
@return array
*/
public function
getJWTCustomClaims()
{
return [];
}
/**
* 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',
];

/**
* The attributes that should be cast to native types.
*
*
@var array
*/
protected
$casts = [
'email_verified_at' => 'datetime',
];
}

Let’s make some APIs for login and logout and get the logged-in user

The AuthController should look like this :

<?php

namespace
App\Http\Controllers;

use Illuminate\Http\Request;

class AuthController extends Controller
{
/**
* Create a new AuthController instance.
*
*
@return void
*/
public function
__construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}

/**
* Get a JWT via given credentials.
*
*
@return \Illuminate\Http\JsonResponse
*/
public function
login()
{
$credentials = request(['email', 'password']);

if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}

return $this->respondWithToken($token);
}

/**
* Get the authenticated User.
*
*
@return \Illuminate\Http\JsonResponse
*/
public function
me()
{
return response()->json(auth()->user());
}


/**
* Get the token array structure.
*
*
@param string $token
*
*
@return \Illuminate\Http\JsonResponse
*/
protected function
respondWithToken(string $token): \Illuminate\Http\JsonResponse
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}

So let’s define APIs endpoint in routes/api.php

<?php

use
Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::group([
'prefix' => 'auth'
], function () {
Route::post('login', [\App\Http\Controllers\AuthController::class , 'login']);
Route::post('me', [\App\Http\Controllers\AuthController::class , 'me']);
});

Now you can log in and receive the token

Spatie package

In this project, I used the Laravel-permission package

Installation

composer require spatie/laravel-permission

You should publish the migration and the config/permission.php config file with:

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

Run the migrations: After the config and migration have been published and configured, you can create the tables for this package by running:

php artisan migrate

In Your User Model add Spatie\Permission\Traits\HasRoles trait

use Spatie\Permission\Traits\HasRoles;class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable , HasRoles;
protected $guard_name = 'api';
....

After this, we should create a controller for access levels

php artisan make:controller AccessLevelController

In your system, you may have many roles such as admin, super admin, accountant, …
because of that, you should have a method to add new roles

In AccessLevelController class add “createRole” method

<?php

namespace
App\Http\Controllers;

use Illuminate\Http\Request;
use Spatie\Permission\Models\Role;

class AccessLevelController extends Controller
{
public function createRole(Request $request): \Illuminate\Http\JsonResponse
{
$role = Role::create(['name' => $request->name]);
return response()->json($role);
}
}

But we have constant permissions like “View Articles”, “show Articles”, “Create Articles”, “Update Articles”, “Delete Articles”

‌Byword constant I mean we must have a policy that cannot be changed

to make this happen make a PHP file called access_level.php in /configdirectory

<?php

return
[
'Articles' => [
'View Articles',
'Show Articles',
'Update Articles',
'Create Articles',
'Delete Articles'
]
];

Add a method to create permissions in the database

<?php

namespace
App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;

class AccessLevelController extends Controller
{
public function createRole(Request $request): \Illuminate\Http\JsonResponse
{
$role = Role::create(['name' => $request->name]);
return response()->json($role);
}

public function createPermission()
{
DB::beginTransaction();
try {
$config = config('access_level');
$permissions = [];
foreach ($config as $permission) {
foreach ($permission as $item) {
$permissions[] = $item;
}
}

foreach ($permissions as $permission_name)
{
if (DB::table('permissions')->where('name' , $permission_name)->count() == 0)
{
Permission::query()->create(['name' => $permission_name]);
}
}
DB::commit();
return 'done';
} catch (\Exception $exception) {
DB::rollBack();
return response([
'error' => $exception->getMessage()
]);
}
}
}

Now it is time to assign permissions to a role

add another method to AccessLevelController called assignPermission

public function assignPermission(Role $role, Request $request)
{
$this->validate($request , [
'permission_ids' => 'required|array',
'permission_ids.*' => 'required|exists:permissions,id'
]);
$permissions = Permission::query()->whereIn('id', $request->get('permission_ids'))->get();
$role->syncPermissions($permissions);
return response('permissions were assigned to ' . $role->name . ' role successfully');
}

Finally, give the role to users with “giveRoleToUsers” method

public function giveRoleToUsers(Role $role, Request $request)
{
$this->validate($request, [
'user_ids' => 'required|array',
'user_ids.*' => 'required|exists:users,id',
]);
User::query()->whereIn('id', $request->get('user_ids'))->get()->each(function ($user) use ($role) {
$user->assignRole($role->name);
});
return response($role->name . ' role was assigned to users');
}

create some role and permission then give the role to a user (via postman)

How to implement an access level system in Laravel (Part 2) here

go to part 2

--

--

Hossein Molavi
Hossein Molavi

Written by Hossein Molavi

My name is Hossein and I’m a software developer. I share my useful experiences in coding with you

No responses yet