Search code examples
phplaravelpermissionsrolesauthorize

Laravel authorize for users and roles


I am trying to authorize Users and Roles in such a way that each user cannot have access to another's id when it comes to Editing. For example, if I enter the editing page for My Termins(Meetings)(in this case) http://localhost:8000/admin/termins/2/edit do not be able to access any meeting ID of another user in which I did not create that termin. So, when I manually type http://localhost:8000/admin/termins/1/edit, I can't access it, showing a 403 error.

My current Controller:

 public function edit($id)
    {
        $termin = Termin::findOrFail($id);

        $this->authorize('edit', $termin);

Rest of code...

My policy:

<?php


// app/Policies/TerminPolicy.php

namespace App\Policies;

use App\User;
use App\Termin;
use Illuminate\Auth\Access\HandlesAuthorization;

class TerminPolicy
{
    use HandlesAuthorization;

    public function edit(User $user, Termin $termin)
{
    \Log::info("User ID: {$user->id}, Created By ID: {$termin->created_by_id}");

    return $user->id === $termin->created_by_id;
}

    public function view(User $user, Termin $termin)
    {
        return $user->id === $termin->created_by_id;
    }
}

AuthServiceProvider:

<?php

namespace App\Providers;

use App\Policies\TerminPolicy;
use App\Role;
use App\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        Termin::class => TerminPolicy::class,
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
        $user = \Auth::user();

        
        if (! app()->runningInConsole()) {
            $roles = Role::with('permission')->get();

            $permissionArray = [];
            
            foreach ($roles as $role) {
                foreach ($role->permission as $permission) {
                    $permissionArray[$permission->title][] = $role->id;
                }
            }

            foreach ($permissionArray as $title => $roles) {
                Gate::define($title, function (User $user) use ($roles) {
                    return count(array_intersect($user->role->pluck('id')->toArray(), $roles));
                });
            }

        }
    }
}

User Model:

public function role()
    {
        return $this->belongsToMany(Role::class, 'role_user');
    }

Rest of code...

With this code, I always get error 403 This action is unauthorized when entering the edit page from any user. But, as I said, each user have to edit and see only their own Termin.

Thank you.


Solution

  • The easiest solution would be to create a relationship between users and termin and access termin via auth()->user(). That way if Termin doesn't belong to the user they won't be able to access it.

    public function edit($id)
    {
        $termin = auth()->user()->termins()->findOrFail($id);
        $this->authorize('edit', $termin);
        
        //..Rest of the code
    }
    

    You already have created_by_id which stores user id so you could just define relationship like this

    public function termins()
    {
        return $this->hasMany(Termin::class, 'created_by_id');
    }
    

    Since you already defined relationship between users and termins you can also replace your policy

    class TerminPolicy
    {
        use HandlesAuthorization;
    
        public function edit(User $user, Termin $termin)
        {
            return $user->termins->contains($termin);
        }
    
        public function view(User $user, Termin $termin)
        {
            return $user->termins->contains($termin);
        }
    }