I'm trying to prevent some users from accessing admin panel.
I have created a middleware with this code called RequireAdminRole
public function handle($request, Closure $next, $guard = null)
{
$user = Auth::user();
dd($user);
if (!$user) {
return $next($request);
}
if (!$user->hasRole('Admin')) {
// if your sessions are decoupled from the frontend
// you can even logout the user like so:
// auth()->logout();
abort(403, 'Access denied');
}
return $next($request);
}
I have added it to kernel.php
'web' => [
\App\Http\Middleware\RequireAdminRole::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
My problem id $user has null value, so I can't get user permissions.
Can anybody help me?
Best regards.
The order of your middleware is important, the middleware handlers are executed in the order that they are defined. This means that you are trying to retrieve the authenticated user from the session before the StartSession middleware has been executed.
So to solve your issue, you should move your RequireAdminRole middleware to the end of the array, or at least after the definition of StartSession.
'web' => [
// Other middleware definitions...
\App\Http\Middleware\RequireAdminRole::class
],
Related
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?
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
I'm trying to create permissions and roles for users in a Laravel API Project. I'm using Passport and Spatie's Permission package. Using that I added a middleware:
Route
Route::middleware('auth:api')->group(function () {
/** Roles Route */
Route::get('roles',[RoleController::class, 'index'])->middleware('permission:role-list');
}
Handler.php:
public function render($request, Throwable $exception)
{
if ($exception instanceof UnauthorizedException) {
return response()->json(['User does not have the right roles.'],403);
}
return parent::render($request, $exception);
}
Controller:
function __construct()
{
$this->middleware('permission:role-list', ['only' => ['index']]);
$this->middleware('permission:role-create', ['only' => ['store']]);
$this->middleware('permission:role-edit', ['only' => ['update']]);
$this->middleware('permission:role-delete', ['only' => ['destroy']]);
}
public function index()
{
$roles = Role::all();
return response(['role' => $roles], 200);
}
I created an admin account and it was given all the existing permissions, but whenever I ask for the route, it shows me that error message:
User does not have the right roles.
I checked the database and the user had all the required permissions for that.
First of all add the package middlewares in the Kernel.php as described here.
protected $routeMiddleware = [
// ... other middleware
'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
];
Depending on where you want to define the permissions to access a method either define them in the Controller or on the route. Adding them to both the route and Controller is only doing the same thing.
Changing something in the Handler.php should not be required.
Note that permissions can have different guards. For example it can be web or api.
After changing roles or permissions it sometimes helps to reset the cache. Resetting the cache can be done with php artisan permission:cache-reset command.
How can I restrict access to admin login page based on IP address in LARAVEL?
I want to set a permission to the admin login page to a single IP Address.
you can use Request::ip(); and check it in middleware.. below is basic poc
middleware
class AdminAccessCheck
{
public function handle($request, Closure $next)
{
$ip = $request->ip();
if ($ip === config('admin.ip')) {
return $next($request);
}
return response()->error();
}
}
kernel.php
protected $routeMiddleware = [
...
'admin.ip_check' => \App\Http\Middleware\AdminAccessCheck::class,
];
web.php
Route::middleware(['admin.ip_check'])->group(function() {
//
});
If you prefer package, you can checkout this repo .. Firewall
Create a middleware
php artisan make:middleware IpMiddleware
Code:
<?php
namespace App\Http\Middleware;
use Closure;
class IpMiddleware
{
public function handle($request, Closure $next)
{
if ($request->ip() != "192.168.0.155") {
// here instead of checking a single ip address we can do collection of ips
//address in constant file and check with in_array function
return redirect('home');
}
return $next($request);
}
}
Register the Middleware in app/Http/Kernel.php file
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'ipcheck' => \App\Http\Middleware\IpMiddleware::class,
];
then apply middelware to routes
Route::get('/', ['middleware' => ['ipcheck'], function () {
// your routes here
}]);
I have been struggling with this from quiet a time now, what i am trying is to redirect all the url's hit by non-logged in users to login page and it gives me this error, which I am sure is because it is creating a loop on /login URL. authentication is checking for authorized user in login page also. however I wish the login page should be an exception when checking the auth. I may be doing something wrong which I am not able to get. here goes my code.
routes.php
Route::post('login', 'Auth\AuthController#login');
Route::get('login' , 'Auth\AuthController#showLoginForm');
Route::get('/' , 'Auth\AuthController#showLoginForm');
kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'can' => \Illuminate\Foundation\Auth\Access\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'acl' => \App\Http\Middleware\CheckPermission::class,
];
Authenticate class
class Authenticate
{
public function handle($request, Closure $next, $guard = null) {
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
}
AuthController class
class AuthController extends Controller {
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
protected $redirectTo = '/dashboard';
protected $loginPath = '/login';
protected $redirectPath = '/dashboard';
public function __construct(){
$this->middleware('auth', ['except' =>'login']);
/* I have been trying these many things to fix this, all in loss.
// $this->middleware('acl'); // To all methods
// $this->middleware('acl', ['only' => ['create', 'update']]);
// $this->middleware('guest', ['only' => ['/login']]);
// echo "Message"; exit;
// $this->middleware('auth');
// $this->middleware('auth', ['only' => ['login']]);
// $this->middleware('auth', ['only' => ['/login']]);
// $this->middleware('auth', ['except' => 'login']);
// $this->middleware('guest');
// $this->middleware('guest', ['only' => ['logout' , 'login', '/login', '/']]);
}
Please help me, It going all above my head, seems some sort of rocket science to me. well btw I am new to laravel and may be doing some silly thing around, apologies for that. Thanks in Advance.
You need add route login outside Laravel group:
routes.php
Route::auth();
Route::group(['middleware' => 'auth'], function () {
// All route your need authenticated
});
Aditionally, you can see yours route list using:
php artisan route:list
Why you are doing all this just to redirect every non-logged in user to login form?
i think you can just do this
Routes.php
Route::post('login', 'Auth\AuthController#login');
Route::get('login' , 'Auth\AuthController#showLoginForm');
Route::get('/' , 'Auth\AuthController#showLoginForm');
Route::group(['middleware' => 'auth'], function () {
// any route here will only be accessible for logged in users
});
and auth controller construct should be like this
AuthController
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
}
The problem is with your routes.
When I enter and I am not logged out you send me to login(get) route. And as you are specifying the middleware in the construct function in the AuthController, every time a method of the AuthController is called, construct function is called again and sends you back at login.. and it repeats indefinitely.
like #mkmnstr say
The problem is with your routes.
When I enter and I am not logged out you send me to login(get) route. And as you are specifying the middleware in the construct function in the AuthController, every time a method of the AuthController is called, construct function is called again and sends you back at login.. and it repeats indefinitely.
to fix that u should add
Auth::logout();
Here
...
} else {
Auth::logout(); // user must logout before redirect them
return redirect()->guest('login');
}
...
If your working with custom middleware you must follow it's all rules
in my case, I have to define a custom route class in the web middleware group.
In the world of copy-paste sometime we make mistakes.
Middleware :
public function handle($request, Closure $next)
{
if(!isset(session('user'))){
return redirect('login');
}
return $next($request);
}
}
My Mistake in Kernel.php
if custom middleware class present in web $middlewareGroups will check condition 2 times so it will give error as: redirected you too many times
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\webUser::class, //Remove
],
protected $routeMiddleware = [
'webUser'=> \App\Http\Middleware\webUser::class //Keepit
]
I had same problem after creating my own route service provider. The problem was that when I tried to login, in first time login page showed and after entering credentials I encountered "redirected too many times" and redirected to my admin dashboard and login route!
the solution was: adding middleware "web" into my routes:
Route::middleware('web')->group(base_path('Admin/routes.php'));
I am newcomer in Laravel. I have a problem with session flash in Middleware.
In Middleware:
public function handle($request, Closure $next)
{
if(auth()->check()){
return $next($request);
}else{
Notification::error('Please login');
return redirect()->route('admin.auth.login.get')->with('test',' session');
}
}
In View:
I get Nofitication but nothing happen. I check with flash session (session('test'))-> nothing happen.
Please help to explain me why it dont work? and what's solution?.
Thank you very much and sorry about my English.
Add your route in web middleware
Route::group(['middleware' => ['web']], function () {
//
});
See this
basic-routing
Make sure that in kernel.php
web Middleware is
'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,
],