Laravel Multiple Middleware in Route with OR Condition - php

I wonder if I can do this in Laravel Route. Let's say I have Admin, Premium and User (which can be login too by using Auth) Middleware. Also, I have controller with methods like this: index, create, edit, delete and I want Admin to be able do all those things, but Premium can only be able to access index method, and User can't access anything in this controller (he can access another controller). I know I can use except or only middleware method like this:
public function __construct()
{
$this->middleware('premium')->only('index');
$this->middleware('admin');
// or maybe $this->middleware('admin')->except('index');
}
but when I try to put these two middlewares in __construct method they will start to conflict each other, it makes sense because index method can be access by Premium but then can't be access by the Admin itself. By the way, my middleware is simply checking:
if (Auth::check()) {
if (Auth::user()->role == 'Admin') {
return $next($request);
}
}
return redirect('/home');
So, back to my question, can I have OR Middleware so I can avoid conflict from multiple middleware (which is must be AND condition when they written at the same controller constructor)?

If you change up the way your logic is thinking a little bit, the answer becomes pretty easy. You can create new middleware that checks if it can access the specific method.
So create the following middleware 'CanAccessIndex':
if (Auth::check()) {
if (Auth::user()->role == 'Admin' || Auth::user()->role == 'Premium') {
return $next($request);
}
}
return redirect('/home');
Then, you can put that middleware on the index function (instead of the premium middleware) and put your admin middleware on everything EXCEPT index. Like so:
public function __construct()
{
$this->middleware('canAccessIndex')->only('index');
$this->middleware('admin')->except('index');
}
That's one way to do it.

You need middleware group for this, and to manage these hierarchy of access layer, you can simply use Route grouping.
I will demo an example of what I mean:
Say you have auth middleware general for authenticated users (i.e everybody), then another called premium for premium member, and admin for the Admin.
Then you'll group based on the access level:
Route::middleware('auth')->group(function(){
Route::middleware('premium')->group(function(){
Route::post('/create', 'HomeController#create')->middleware('admin');
Route::put('/update/{id}', 'HomeController#update')->middleware('admin');
Route::get('/index', 'HomeController#index');
Route::put('/delete/{id}', 'HomeController#delete')->middleware('admin');
});
});
So you can have general check in your middleware with a check. It would have been much easier if you have role level say 3 for admin and 2 for premium member. So we can have for the premium middleware:
public function handle($request, Closure $next)
{
return auth()->user->role >= 2
? $next($request)
: redirect('/home');
}
This is just an example. You can do further check based on your need but more importantly, ensure your admin middleware to checks the exact role level that is allowed.

Related

Apply middleware conditionally based on auth in Laravel

I am trying to achieve the following:
Create a route with 2 groups of middleware. 1 applies when not logged in and the other applies when you are.
The auth middleware obviously catches this but I want to be able to reach the route without auth.
Route::get('my-route', ...)
->middleware('not-auth')
->middleware('when-authed')
->name('my.route');
The route should be accessible when not logged in with NO other middleware applied
The route should have other middleware applied when logged in which will include auth:sanctum checks
I am happy to apply custom logic in the controller or the routes file
I have tried using the constructor in the controller but auth is not available to check if the user is logged in at time of instantiation.
As an example of something i have tried to demonstrate what I am trying to achieve, see below:
$authMiddleware = [
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
'shared.ui'
];
Route::get('/route', [class::class, 'index'])
->middleware((auth()->check()) ? $authMiddleware : [])
->name('route');
Right, I have rewritten the verified middleware to resolve my issue and applied it as a different middleware on the route. This then resolves the issue I had where you had to be logged in with the middleware.
See below:
public function handle($request, Closure $next, $redirectToRoute = null)
{
//removed from start of if statement on original verify middlware !$request->user() ||
if (($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::guest(URL::route($redirectToRoute ?: 'verification.notice'));
}
return $next($request);
}
Added alias to route middleware
'auth.verified' => \App\Http\Middleware\EnsureEmailIsVerifiedWhenLoggedIn::class,
Applied auth.verified on the route in question
Thank you to ericmp for suggesting I should be looking at doing this a different way and thanks to the rest of you for your comments.

Laravel: Check if User is an admin using isAdmin column?

I'm really new to laravel and have been reading the documentations and tutorials. I'm planning on making an app that has 2 roles, admin and user. I modified my User model to have the column 'isAdmin' with boolean value since I only need 2 roles. How do I perform a check on this attribute during auth? Thank you.
TO answer your question, first of all to make protect any route using the auth middleware which ensures a user is authenticated (logged in) before they can access the route, you simply need to add the auth middleware.
e.g
web.php
<?php
Route::middleware('auth')->group(function(){
//All Routes which needs user to be logged in
});
or
//Individiual Route Middleware
Route::get('/path/to', 'controller#instance')->middleware('auth');
As for checking user role, you can basically create a middleware for this using the following steps:
run your php artisan make:middleware IsAdminMiddleware
open your IsAdminMiddleware and add this code inside the handle function:
public function handle($request, Closure $next)
{
if(!Auth::check()){
return redirect()->route('login');
}
if(Auth::user()->isAdmin == true){
return $next($request);
}
return redirect()->back()->with('unauthorised', 'You are
unauthorised to access this page');
}

how to block Url to all user except super admin

it should forbid that the user grabs this url:
?main_title=banner
?main_title=law
?main_title=faq
with this
if(\Auth::user()->hasRole(['super_admin']))
I am going to assume that you are using spatie/laravel-permission based on your example code.
Laravel Permission comes with built-in role middlewares
One of the ways you could use them is by grouping the routes you want to be accessible only by super admins
Route::group(['middleware' => ['role:super_admin']], function () {
// YOUR ROUTES HERE
});
It's always good to using the middlewares ,
So in your case first create a Trait for roles
public function isSuperadmin(){
return Auth::user()->role->role=='superadmin';
}
After that create a middlewar like superadmin for the superuser and in that first include your trait
use App\Traits\Roles;
after that
use Roles;
public function handle($request, Closure $next)
{
if(!$this->isSuperadmin())
{
return back();
}
return $next($request);
}
and just register the middleware in the app/http/kernal.php in protected $routeMiddleware function
'superadmin' => \App\Http\Middleware\superadmin::class,
so it's make your life very easy now you don't need to check the url or role every time , for any url you want to block for other users just use
Route::get('/?main_title=law', 'HomeController#function')->middleware('superadmin')->name('admin-dashboard-home');
so if the user role is superadmin then he is allow to assess the url you can redirect the other users or show the error message :)

Laravel: Checking if user has permission

I'm writing a server based solution. In database there are many users with different permissions and I have to check if they have permission to access module they are trying to.
In every Controller I have included something like:
protected $module = "moduleName";
I tried to solve it like:
function __construct()
{
$perm = session()->get('perm');
if (!isset($perm[$this->module]) || !$perm[$this->module]) {
Session::flash('message_error', "<span class='glyphicon glyphicon-warning-sign'></span> Access denined!");
return back();
}
}
It shows the message but it still displays the page not redirects back.
As you see I'm reading permissions from session and modules name is saved in controller so I don't think this could be solved by middleware unless I'm making middleware for each module (I'm talking about 30 modules).
Thanks for taking the time to read this
Middleware actually solved this.
Route:
Route::group(['middleware' => 'module:moduleName'], function () {
// Routes...
});
Custom middleware:
public function handle($request, Closure $next, $module)
{
$perm = session()->get('perm');
if (!isset($perm[$module]) || !$perm[$module]) {
Session::flash('message_error', "<span class='glyphicon glyphicon-warning-sign'></span> Access denined!");
return redirect()->back();
}
return $next($request);
}
Also I'll mention that Route groups can be nested. So you can wrap multiple groups with something like auth middleware as well
There is a very easy fix to your code, you forgot the define the redirect, so instead of using
return back();
use
return redirect()->back();
This will do the redirect.
There is also a mistake in your reasoning, you could and probably should use middleware.
Middleware does have access to the user, session and can be passed parameters. These are the necessary requirements for your system.
You can also assign middleware on a controller basis.

Multiple middleware has permittion on same routes

I have multi middleware (studen, parent, admin) and create some route group with that middleware. But some route can access if user is in any of that groups and belong in any of that middleware but not if is in other middleware fot example teacher. I use something like that in docs: http://laravel.com/docs/5.1/routing#route-groups But it's work when I put one route, when add another route group with another middleware it doesn't work. Is that possible and how to make it?
When I execute php artisan route it gives me an error
[Symfony\Component\Debug\Exception\FatalErrorException]
Call to a member function inRole() on null
Laravel's route middleware is executed one by one as declared in routes.php file. Therefore, if one of them denies access by throwing an exception or returning some respoise, the next middlewares won't be executed.
In order to make that work, you'll need a single middleware that would check if current user has any of required roles. Luckily, as of Laravel 5.1 you are able to pass parameters to middleware from your routes.php file (see http://laravel.com/docs/5.1/middleware#middleware-parameters), so you'll only need one middleware class to handle all cases.
Example middleware class could look like that:
class HasAnyRole
{
public function handle($request, Closure $next, $roles)
{
// Return Not Authorized error, if user has not logged in
if (!$request->user) {
App::abort(401);
}
$roles = explode(',', $roles);
foreach ($roles as $role) {
// if user has given role, continue processing the request
if ($request->user->hasRole($role)) {
return $next($request);
}
}
// user didn't have any of required roles, return Forbidden error
App::abort(403);
}
}
Register the middleware in your Kernel.php:
protected $routeMiddleware = [
'has_any_role' => 'App\Http\Middleware\HasAnyRole',
];
Now, in your routes.php you can apply the middleware to a group like that:
//this route is available only to users with role admin or author
Route::put('post/{id}', ['middleware' => 'has_any_role:admin,author', function ($id) {
//
}]);
This should do the trick, just make sure that your User class has a hasRole method.

Categories