2

I have made a Laravel 8 application (link to GitHub repo) that requires user registration and login.

I am currently working on adding user roles and permissions. I have 3 roles (types of users): Admin, Author, and Member. Each type of user should have access to a section of the dashboard.

The users table:

enter image description here

The roles table:

enter image description here

In routes\web.php I have:

Route::get('/', [HomepageController::class, 'index'])->name('homepage');

Auth::routes();

Route::group(['middleware' => ['auth']], function() {
    Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
    Route::get('/dashboard/profile', [UserProfileController::class, 'index'])->name('profile');
    Route::match(['get', 'post'],'/dashboard/profile/update', [UserProfileController::class, 'update'])->name('profile.update');
    Route::post('/dashboard/profile/deleteavatar/{id}/{fileName}', [UserProfileController::class, 'deleteavatar'])->name('profile.deleteavatar');

    //User roles
    Route::get('/dashboard/author', [AuthorController::class, 'index']);
});

In the User model (app\Models\User.php) I have:

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'role_id',
        'username',
        'first_name',
        'last_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',
    ];

    public function roles() {
      return $this->belongsToMany(Role::class);
    }

    public function users()
    {
        return $this
            ->belongsToMany('App\User');
    }

    public function authorizeRoles($roles)
    {
      if ($this->hasAnyRole($roles)) {
        return true;
      }
      abort(401, 'This action is unauthorized.');
    }

    public function hasAnyRole($roles)
    {
      if (is_array($roles)) {
        foreach ($roles as $role) {
          if ($this->hasRole($role)) {
            return true;
          }
        }
      } else {
        if ($this->hasRole($roles)) {
          return true;
        }
      }
      return false;
    }

    public function hasRole($role)
    {
      if ($this->roles()->where('name', $role)->first()) {
        return true;
      }
      return false;
    }
}

In the AuthorController (Controllers\Dashboard\AuthorController.php)

class AuthorController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('role:ROLE_Author');
    }

    public function index()
    {
        return view('dasboard.author');
    }
}

As the CheckRole middleware shows, if the user is not authorised, the message should be "This action is unauthorized":

class CheckRole
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next, $role)
    {
        if (!$request->user()->hasRole($role)) {
            abort(401, 'This action is unauthorized.');
        }
        return $next($request);
    }
}

The problem

For a reason I have not been able to find out, trying to redirect an author to it's section of the admin panel results in a 403 error:

User does not have any of the necessary access rights.

Question

What am I doing wrong?

Razvan Zamfir
  • 4,209
  • 6
  • 38
  • 252

4 Answers4

1

After Reviewing your code .I have found few mistakes

1.In AuthorController you have passed role as ROLE_Author instead of Author

 $this->middleware('role:ROLE_Author');

But in db you have named role name as Author so it should be

$this->middleware('role:Author');

2.In User model you have hasRole($role) method but it is accessing role relationship which has belongsToMany relationship

 public function roles() {

  return $this->belongsToMany(Role::class);
}

So if you check db role_user has empty record.So add related data in role_user table but now you are adding role in users table.

Suppose if you are looking for assigning role in user table then change relation role in User table

 public function roles() {
      return $this->belongsTo(Role::class,'role_id','id');
    }

if user has no access rights then it throws below error otherwise it goes to dashboard.

401 UNAUTHORIZED

Also in MemberController you have to change middleware from

  $this->middleware('role:ROLE_Member');

to

  $this->middleware('role:Member');

Also in AuthorController.you have error in blade file name. view('dasboard.author') but if you see view folder then you have named folder as dashboard but in view you have mentioned dasboard.author So change from this

 public function index()
 {
    return view('dasboard.author');
 }

to

 public function index()
 {
    return view('dashboard.author');
 }

Note: After reviewing I didn't found mentioned error message "User does not have any of the necessary access rights." in git repo.Also it doesnt throw 403 error for unauthorized users. So try to clear view cache ,browser cache.

John Lobo
  • 14,355
  • 2
  • 10
  • 20
  • It does not work. I get a `View [.dasboard.author] not found.` error. ` – Razvan Zamfir Jun 21 '21 at 13:27
  • @RazvanZamfir . yes you have typo error so it should be public function index() { return view('dashboard.author'); } . check my updated answer – John Lobo Jun 21 '21 at 13:55
  • I still see `User does not have any of the necessary access rights` instead of `This action is unauthorized`. – Razvan Zamfir Jun 21 '21 at 15:01
  • i have checked your project.you dont have any such error code message.But make sure you have same codebase with which you provided in branch https://github.com/Ajax30/Larablog/tree/user_roles – John Lobo Jun 21 '21 at 15:04
  • i have verified entire codebase.No such message .If so then once you verify whether you have provided same codebase.If so which user you tried so i can try same user – John Lobo Jun 21 '21 at 15:05
  • i have verified all user too but no such errors other than unauthroized. also dont have such errors which you mentioned ."User does not have any of the necessary access rights instead of This action is unauthorized".look like you have given wrong codebase to us.can you verify once – John Lobo Jun 21 '21 at 15:15
  • also clear cache and view .run following command. php artisan view:clear ,php artisan cache:clear php artisan config:clear ,php artisan clear – John Lobo Jun 21 '21 at 15:31
  • In `app\Models\User.php`, at line 62, thre is `abort(401, 'This action is unauthorized.');` – Razvan Zamfir Jun 21 '21 at 19:33
  • @RazvanZamfir.yes its 401 not 403 .also it wont call since method never used anywhere. – John Lobo Jun 22 '21 at 01:19
  • @RazvanZamfir.do you still have issue ? – John Lobo Jun 24 '21 at 16:31
  • Yes, I still gave those issues, even after running the clear commands you mentioned. – Razvan Zamfir Jun 27 '21 at 14:29
  • @RazvanZamfir. as i checked multiple times your code .no such errors and even it works well after fixing mentioned issues.you can cross verify code again or checkout code from github and create new project .so verfiy once. – John Lobo Jun 27 '21 at 14:30
  • Are you sure you checked the `user_roles` branch? – Razvan Zamfir Jun 27 '21 at 14:35
  • @RazvanZamfir yes i checked same branch .i am pretty sure.can you search these "User does not have any of the necessary access rights." text in project folder and see if its there .if its there then create new branch and upload it with sql .i will check again – John Lobo Jun 27 '21 at 14:37
1

The main problem that you didn't get the roles properly here's some changes according to that:

At Author Controller

1- Update $this->middleware('role:ROLE_Author'); => $this->middleware('role:Author');

2- Update return view('dasboard.author'); => return view('dashboard.author');

At Role Model

1- Add public $timestamps = false;

2- Add public function users() { return $this->hasMany(User::class); }

At User Model

1- update

public function roles() { return $this->belongsToMany(Role::class); }

To be:

public function role() { return $this->belongsTo('App\Models\Role'); }

2- $this->roles()->where('name', $role)->first() => $this->join('roles', 'users.role_id', 'roles.id')->where('roles.name', $role)->first()

At create user Migration

1- Remove $table->foreignId('role_id')->constrained('roles'); 2- Add $table->unsignedInteger('role_id')->nullable(); $table->foreign('role_id')->references('id')->on('roles');

(Note: If your application is on production then you need to create another migration to add the new field instead of add the field to old migration)
1

Where is your permissions table and role_has_permissions table . You only save role yet but you are not giving any role permission . So it gives you 403 error.

Use this samples :- https://spatie.be/docs/laravel-permission/v4/introduction

sourabh singh
  • 167
  • 1
  • 16
1

Since laravel 7 you have the method id(), so you dont have to use $table->increments('id'); (it caused me errors with foreign keys constraints).

EnriqueRBT
  • 74
  • 9