I use Laravel 9.
I have this route , included in a group and middleware :
Route::middleware(['auth', 'ensureUserIsAdmin'])->group(function () {
.....
Route::resource('sports', SportController::class);
.....
});
The middleware is only to check if the user is or not an administrator.
When the connected user is not admin, and tries to go to this route for a known sport
/sports/FOOT/edit
then he receives the response "FORBIDDEN". Perfect, the middleware made his job.
But when the same not admin user tries to go to a route for an unknown sport
/sports/UNKNOWSPORT/edit
then he receives the response "NOT FOUND". Is it normal ? It looks like the framework makes a database request and only after he applies the middleware.
What's wrong in my code ?
CodePudding user response:
Every route within web.php
file is processed by web
group middleware - you may find this in RouteServiceProvider
class
Route::middleware('web')
->group(base_path('routes/web.php'));
This group defined within Kernel
class and contains some app middleware. They're handled before any route specific middleware fires
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
So when you think your route has 'auth', 'ensureUserIsAdmin' as middleware only it is not quite true
In your case \Illuminate\Routing\Middleware\SubstituteBindings::class
middleware is the one is failing and showing 404
. And yes, it makes query to database if there are some route parameters in it
For simplicity, let say you have this route and only model with id=1 exists
Route::get('/sports/{sport}', function (Sport $sport) {
dump($sport);
});
/sports/1
is dumping model, sports/2
shows 404
as expected. Let's comment \Illuminate\Routing\Middleware\SubstituteBindings::class
Now both pages are actually dumping model, but sports/2
shows empty default model with no parameters
If you need to change this logic and show 403
for non-existing models, you may add middleware to a group BEFORE \Illuminate\Routing\Middleware\SubstituteBindings::class
middleware
For example, lets' create simple middleware which always return 403
like
public function handle(Request $request, Closure $next)
{
abort(403);
}
And change web
group to
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
AlwaysFalse::class, // HERE I AM
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
Now no matter what both pages will show 403
But this will be applied for EVERY route within routes/web.php
file, so you may change the logic like
public function handle(Request $request, Closure $next)
{
// Applied only in any route named `sports.something` - resource routes including
// is_admin() function doesn't really exists, middleware logic is up to you
abort_if((Route::is('sports.*') && !is_admin()), 403);
return $next($request);
}
Now for admin user /sports/1
shows model, /sports/2
- 404
response.
For non-admin user both pages will return 403
.
About is it normal or not - I think yes. Ask yourself - what can you do (what your access level) over a thing which is doesn't even exists? So it better define first does model really exists and after you're sure it is, make something with it. But it is just my opinion, someone may disagree
Hope it'll help
CodePudding user response:
I don't know how your middleware is build but i normally do it like this.
in User Model
public function isAdmin(){
foreach($this->roles as $role){
if($role->name == 'administrator' && $this->is_active == 1 ){
return true;
}
}
}
in Middleware
public function handle(Request $request, Closure $next)
{
if(Auth::check()){
if(Auth::user()->isAdmin()){
return $next($request);
}
}
return redirect('/');
}