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

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 the “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)

To view or add a comment, sign in

More articles by Hossein Molavi

Insights from the community

Others also viewed

Explore topics