Add Multiple Middleware to Laravel Project - php

I'm new to laravel I have created middleware for my each role but when I add it to my route it won't work.
If I add single middleware to my route it works fine but when I add second and third one It will not work.
It won't shows the route to authorized user it redirect it to home,
My User Model:
public function IsAdmin()
{
if($this->role_id =='1')
{
return true;
}
else
{
return false;
}
}
public function IsManager()
{
if($this->role_id =='2')
{
return true;
}
else
{
return false;
}
}
public function IsUser()
{
if($this->role_id =='3')
{
return true;
}
else
{
return false;
}
}
My Kernal:
'IsAdmin' => \App\Http\Middleware\IsAdmin::class,
'IsManager' => \App\Http\Middleware\IsManager::class,
'IsUser' => \App\Http\Middleware\IsUser::class,
My IsAdmin Middlewares:
public function handle($request, Closure $next)
{
$user =Auth::User();
if(!$user->IsAdmin())
{
return redirect('stock');
}
return $next($request);
}
My IsManager
public function handle($request, Closure $next)
{
$user =Auth::User();
if(!$user->IsManager())
{
return redirect('stock');
}
return $next($request);
}
and IsUser
public function handle($request, Closure $next)
{
$user =Auth::User();
if(!$user->IsUser())
{
return redirect('stock');
}
return $next($request);
}
and finally my Route
Route::get('approv',['middleware'=>['IsManager','IsAdmin'],function(){
return view('approv');
}]);

This will not work as you'd expect. All middleware need to pass in order for the request to be processed which means that your user will need to be both a manager and an admin at the same time which based on your setup is impossible.
You can get around this (kind of) by making a different kind of middleware:
Kernel:
'roles' => \App\Http\Middleware\Roles::class,
And the Roles middleware:
class Roles {
private function checkRole($role) {
switch ($role) {
case 'user': return \Auth::user()->IsUser();
case 'manager': return \Auth::user()->IsManager();
case 'admin': return \Auth::user()->IsAdmin();
}
return false;
}
public function handle($request, Closure $next, ...$roles)
{
foreach ($roles as $role) {
if ($this->checkRole($role)) {
//At least one role passes
return $next($request);
}
}
//All checks failed so user does not have any of the required roles
return redirect('stock');
}
}
Then to use this you simply do:
Route::get('approv',['middleware'=>['roles:manager,admin'],function(){
return view('approv');
}]);
This works because Laravel Middleware support parameters. You can pass parameters as a comma separated list of strings where you declare the middleware. In this case this was done as roles:manager,admin
Laravel will then send these parameters as additional parameters in the handle method. These can be accessed using PHPs syntax for variadic arguments. In this particular case it's by using the array spread operator. This is documented as an example in the function arguments section of the PHP manual.
Note that this is actually equivalent to saying :
public function handle($request, Closure $next, $role1=null, $role2=null, $role3=null)
but using the spread operator is much more convenient since ...$roles would be an array which contains only the roles that were passed in the middleware.

Related

Add functionality to enable or disable user to use specific features on basis of invoice Assigned

I am working on a project in which I have three type of users Admin and user1 and user2. I want user1 and user2 to able to use certain features in application only if the admin has assigned an invoice to them. I have tried using helper function given below.
$invoice = Invoice::pluck('user_id')->toArray();
if (Auth::user()->admin == 1 || in_array(Auth::user()->id, $invoice)) {
return 1;
} else {
return 0;
}
but this does not work fine. I'll have to place it before every method of a controller in order to restrains users to use that feature. Is there any thing else I can do?
Any Better Approach for this?
You can use middlewares.
Create your middleware with
php artisan make:middleware UserWithInvoiceMiddleware
Then open your file in app/Http/Middleware/UserWithInvoiceMiddleware.php, and add this to the handle method:
public function handle($request, Closure $next, ...$guards)
{
$user = auth()->user();
$invoice = Invoice::pluck('user_id')->toArray();
if ($user->admin || in_array($user->id, $invoice)) {
return $next($request);
}
return response()->json(['message' => 'Request not authorized.'], 401);
}
Also, you can create a relation in your user model with the Invoice model:
public function invoice()
{
return $this->hasOne(Invoice::class);
}
Then, you can simplify your middleware using this relation:
public function handle($request, Closure $next, ...$guards)
{
if (auth()->user()->admin || auth()->user()->has('invoice')) {
return $next($request);
}
return response()->json(['message' => 'Request not authorized.'], 401);
}
You have to register your middleware in app/Http/Kernel.php, under the $routeMiddleware array:
protected $routeMiddleware = [
...
'user-with-invoice' => App\Http\Middleware\UserWithInvoiceMiddleware::class,
];
Then, you can protect your routes with this middleware, adding a ->middleware('user-with-invoice') to the routes where the user has to be an admin or have an invoice:
Route::get('/example', ExampleController::class)->middleware('user-with-invoice');
you can use make a middleware and pass requests throw it to check if the user is authorized to do that or not.
class SomeMidllewareName
{
/**
* Handle an incoming request.
*
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$invoice = Invoice::pluck('user_id')->toArray();
if (1 == Auth::user()->admin || in_array(Auth::user()->id, $invoice)) {
return $next($request);
}
return \response()->json(['message' => 'you are not authorized'], 401);
}
}
then, you can validate on the routes and you can use also policies and validate every feature alone

How to send a string or an array to laravel middleware

I'm working on Laravel and try to make anACL system. I have a Role Middleware From myController I've sent Role to my RoleMiddleware two way.
first one is send string.
$this->middleware('HasRole:User|Admin|Author');
this way i'm get a string when use dd() function.
and the second way is.
$this->middleware('HasRole:User,Admin,Author');
this way i'm get an array when use dd() function.
but this array only contains a single value.
result like this.
array:1 [▼
0 => "User"
]
other two value Admin & Author doesn't appear in this array.
How can i work both way string & array
Here is my middleware.
public function handle($request, Closure $next,$role='')
{
$roles=is_array($role)? $role: explode('|', $role);
dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
I know maybe not exactly the answer to your question, but I recommend you to use laravel authorization instead middleware. see this doc
I prefer to use this code for your problem in the controller:
$this->authorize('update', $post);
or:
if ($user->can('create', Post::class)) {
// Executes the "create" method on the relevant policy...
}
then inside the policy file, you should specify the statement like:
if ($user->isSuperAdmin() || $user->id === $post->user_id) {
return true;
}
if you want middleware to automatically convert your string to array then middleware $role params should be like this
public function handle($request, Closure $next, ...$roles)
{
dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
Then use it like this $this->middleware('HasRole:User,Admin,Author');
OR
But if you want some other character to separate your roles like | then the code should be
public function handle($request, Closure $next, $role = '')
{
$roles = explode('|', $role);
dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
And use it like $this->middleware('HasRole:User|Admin|Author');
You can use both, just add some code in your middleware.
public function handle($request, Closure $next,...$role)
{
//add two line
$str_role=explode('|',$role[0]);
$roleString=$str_role;
$roles=is_array($role)? $role : is_array($roleString)? $roleString : null;
//dd($roles);
if($request->user()===null)
{
return response('Insufficient Access',401);
}
if($request->user()->hasAnyRole($roles) || !$roles)
{
return $next($request);
}
return response('Insufficient Permission',401);
//return $next($request);
}
NOW, you can use.
$this->middleware('HasRole:User|Admin|Author');
OR,
$this->middleware('HasRole:User,Admin,Author');

How to check user Permissions using Custom Middleware in Laravel

I'm developing a Laravel ACL System. My base Table's are users,roles,permissions and pivot tables are role_user,role_permission,user_permission.
I want to check User Permissions using my custom middleware HasPermission. I have tried this way but it's not working properly. every user can access the all the permissions which have or have not.
Now, How can I solve the issue. Please see my code sample.
My Controller.
function __construct()
{
$this->middleware('auth');
$this->middleware('HasPermission:Role_Read|Role_Update|Role_Delete');
}
My Middleware.
class HasPermission
{
public function handle($request, Closure $next,$permissions)
{
$permissions_array = explode('|', $permissions);
// $user = $this->auth->user();
foreach($permissions_array as $permission){
if(!$request->user()->hasPermission($permission)){
return $next($request);
}
}
return redirect()->back();
}
}
and, my User Model method.
public function user_permissions()
{
return $this->belongsToMany(Permission::class,'user_permission');
}
public function hasPermission(string $permission)
{
if($this->user_permissions()->where('name', $permission)->first())
{
return true;
}
else
{
return false;
}
}
Best way to do is that you need to introduce an new service provider and in that you can check the authorization and permissions.
I made a test project (last year) for db driven permission and I used service provider.
That's the perfect way to implement.
Basically !$request->user()->hasPermission($permission) is saying if the user associated with the request does not have this permission the middleware passes, however this is not what you want. Here's what you should do:
If you need the user to have one of the stated permissions you need to do:
class HasPermission
{
public function handle($request, Closure $next,$permissions)
{
$permissions_array = explode('|', $permissions);
foreach($permissions_array as $permission){
if ($request->user()->hasPermission($permission)){
return $next($request);
}
}
return redirect()->back();
}
}
If you want the user to have all stated permissions you need to do:
class HasPermission
{
public function handle($request, Closure $next,$permissions)
{
$permissions_array = explode('|', $permissions);
foreach($permissions_array as $permission){
if (!$request->user()->hasPermission($permission)){
return redirect()->back();
}
}
return $next($request);
}
}
As an added note if you want to do this in a more elegant way you can do:
class HasPermission
{
public function handle($request, Closure $next, ...$permissions_array)
{
//Function body from above without the explode part
}
}
And
function __construct()
{
$this->middleware('auth');
$this->middleware('HasPermission:Role_Read,Role_Update,Role_Delete');
}
If you use commas then the framework will split the string into arguments for you .
In my case i just added simple function to get permissions from database and then check it Middleware. Check this code:
// Add new function to get permissions from database
public static function user_permissions($user) {
$permissions=DB::table('permissions')->where('user_id', $user)->first();
return $permissions;
}
// In Middleware check your permissions
if(Auth::guest())
{
return redirect('/');
}
elseif(Functions::user_permissions(Auth::user()->id)->user_managment != 1) {
return redirect('/');
} else {
return $next($request);
}
In web.php/api.php:
Route::middleware('hasPermission')->group(function() { // for all routes
Route::get('/article', [ArticleController::class, 'index'])->name('article.index');
});
in middleWare:
class HasPermission
{
public function handle($request, Closure $next)
{
$routeName = Request::route()->getName();
$permission = $user->permissions()->where('route_name', $routeName)->first();
if ( ! empty($permission)){
return redirect()->back();
}
return $next($request);
}
}

RedirectIfAuthenticated redirect if attempt to open other login form

I have two login forms with two different tables.One is default with /login route and the other has route /myportal. I have extra logincontroller
protected $redirectTo = '/student-home';
public function showLoginForm()
{
return view('my_portal');
}
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->flush();
$request->session()->regenerate();
return redirect('/my_portal');
}
protected function guard()
{
return Auth::guard('web_student');
}
public function username ()
{
return 'username';
}
This login is working fine. But, I am having problem with RedirectIfAuthenticated
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
else if(Auth::guard('web_student')->check())
{
return redirect('student-home');
}
return $next($request);
}
Now, if the user is already logged in, it is redirected to /student-home only if the route is /login and not /my-portal. i.e only if i click on regular form not this extra form I created. How can I redirect to student-home if user clicked on /my-portal?
You can connect a controller to the my-portal route with :
Route::get('test', 'exampleController#example') ;
Then in the controller function, you can check if the user is already logged in by
public function example() {
if(Auth::check()) {
//This condition will run if the user is logged in !
return redirect('student-home');
}
//Do whatever you want if user is not logged in!
}
Hopefully, this answers your question!
Please change your RedirectIfAuthenticated middleware like this
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if(guard == 'web_student') {
return redirect('student-home');
}else return redirect('/home');
}
return $next($request);
}
The problem with your code is that the following segment will always true if a user is logged in. You have to check for whether or not a specific guard is set, inside this if statement if you want to redirect them accordingly.
if (Auth::guard($guard)->check()) {
return redirect('/home');
}

laravel one route with multiple controllers based on user role

So i'm trying to achieve something, that seems is impossible. I want some routes in my application to use different controller based on user role. This is the approach i'm trying, but it doesn't work well. The user routes work, but admin routes return and Trying to get property on non object error in the VerifyCsrfToken.php file*
Route::group(array('middleware' => 'isAdmin'), function() {
Route::get('/', 'Admin\TestController#getIndex');
});
Route::group(array('middleware' => 'isUser'), function() {
Route::get('/', 'User\TestController#getIndex');
});
My middlewares
public function handle($request, Closure $next)
{
if(Auth::user()->isAdmin()) {
return $next($request);
}
}
public function handle($request, Closure $next)
{
if(Auth::user()->isUser()) {
return $next($request);
}
}
I've seen some handle this kind of situation, by just handling this in the controllers or even checking the use role inside the routes file, but I would rather use middlwares, so my routes file would be cleaner
You can do something like this:
Route::get('/', function () {
if (auth()->check()) {
if (auth()->user()->isAdmin()) {
return redirect()->route('');
} elseif (auth()->user()->isUser()) {
return redirect()->route();
} else {
return view('index');
}
}
return redirect()->to('login');
});
The error message has probably nothing to do with the code your show.
But using multiple controllers on one route is impossible, I asked the same question once.
But you could just use one controller and handle the authorization in that controller.
For example:
public function getIndex()
{
if(Auth::user()->isAdmin()) {
//Admin
return $this->getAdminIndex();
} else {
//No admin
return $this->getUserIndex();
}
}
protected function getAdminIndex()
{
return view('admin.index');
}
protected function getUserIndex()
{
return view('user.index');
}
But the cleanest way to do it is to just have 2 routes.

Categories