Reputation: 111
I made a similar question hours ago but it haven't recieved much answers so that is the reason I'm asking the same but I will try to maybe word it diferently to maybe make it more clear, I only got one answer in that question but it didn't worked. I'm going to put a link to my previous question if you want to see it.
Can't protect Routes based on role Laravel 8
The problem I have is that I'm trying to use a middleware to allow access to some routes only if they have the respective role.
The code of my middleware is this (the one suggested in the answer provided on my previous question).
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class EnsureUserHasRole
{
/**
* 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()->role($role)->get()) {
// Redirect...
return back();
}
return $next($request);
}
}
The code of my User model is this
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
class User extends Authenticatable
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'role',
'name',
'email',
'password',
'idPersona',
'estado'
];
public function Persona(){
return $this->belongsTo(Persona::class,'idPersona');
}
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'remember_token'
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function scopeRole($query, $role)
{
return $query->where('role', $role);
}
}
Not sure if it can help but my User table in my database its like this
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role` varchar(20) DEFAULT NULL,
`name` varchar(100) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`password` varchar(250) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`remember_token` varchar(255) DEFAULT NULL,
`idPersona` int(11) DEFAULT NULL,
`estado` tinyint(1) NOT NULL DEFAULT 31,
`prueba` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `users_FK` (`idPersona`),
CONSTRAINT `users_FK` FOREIGN KEY (`idPersona`) REFERENCES `personas` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8mb4
I'm testing my middleware with this Route, in this case I'm trying to let only the administrador
to access the route and if not then just return. (the middleware role is the one in the code above).
Route::get('/gestionarMedicos', [PersonaController::class,'mostrarMedicos'])->name('personaMostrarMedicos')->middleware('auth','firstLogin','role:administrador');
The problem is all users can access it. I think its a logic issue because if I display what the if returns with a dd() outside the if it displays false
, even in the administrador
role.
Upvotes: 0
Views: 766
Reputation: 3847
Change your middleware method to this:
public function handle(Request $request, Closure $next, $role)
{
// dd($request->user()->role, $role);
// uncomment this for testing purpose if it's not working, both should be equal to "administrator"
if (!$request->user() || $request->user()->role != $role) {
// Redirect...
return back();
}
return $next($request);
}
What you previsously had, $request->user()->role($role)->get()
, will return a Collection of administrators.
It's not what you are looking for. You just want to check if the current user, $request->user()
, is an administrator.
Since a user has a role
column, just checking if the $role
you provided is the same should be enough.
In english, the if statement says "if there is no user or if the user has a column role
with a value not equal as $role
, then redirect back".
Use a variadic function (https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list).
Add the ...
in function declaration (...$roles
) :
public function handle(Request $request, Closure $next, ...$roles)
{
//dd($roles);
//is now an array with all the roles you provided to the route.
if (!$request->user() || !in_array($request->user()->role, $roles)) {
// Redirect...
return back();
}
return $next($request);
}
Using a variadic function allows you to convert a string argument to an array.
In your route, you can now add multiple roles:
Route::get('/gestionarMedicos', [PersonaController::class,'mostrarMedicos'])->name('personaMostrarMedicos')->middleware('auth','firstLogin','role:administrador,client,somethingelse');
In the middleware, $roles
will be equal to:
['administrador', 'client', 'somethingelse']
Upvotes: 1