Why doesn't this dashboard route follow my middleware logic? - php

I am working on a Laravel 8 app that uses Microsoft Azure for user management (login included).
I began by following this tutorial on their website.
I have these routes "under" the dashboard route, that I want to protect with a piece of custom middleware:
// Dashboard routes
Route::get('/dashboard', [DashboardContoller::class, 'index'])->name('dashboard');
Route::group(['prefix' => 'dashboard' , 'middleware' => ['checkSignedIn']], function() {
Route::get('/users', [UsersContoller::class, 'index']);
Route::get('/create-user', [UsersContoller::class, 'create']);
Route::get('/delete-user/{id}', [UsersContoller::class, 'delete']);
});
The conditions for a user to be allowed to the application's dashboard are:
They sign in with a valid Microsoft account
Their email is inside an aray of alowed emails:
private $allowedEmails = [
'user.one#domain.com',
'user.two#domain.com',
'user.three#domain.com',
];
For this purpose, I have done the flollowing:
Created a CheckSignedIn middleware, with php artisan make:middleware CheckSignedIn.
Registered the above middleware in app\Http\Kernel.php:
protected $routeMiddleware = [
// More middleware
'checkSignedIn' => \App\Http\Middleware\CheckSignedIn::class,
];
In app\Http\Middleware\CheckSignedIn.php I have:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CheckSignedIn {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
private $allowedEmails = [
'user.one#domain.com',
'user.two#domain.com',
'user.three#domain.com',
];
public function handle(Request $request, Closure $next) {
$isSignedIn = null !== session('userName') && in_array(session('userEmail'), $this->allowedEmails);
if (!$isSignedIn) {
return redirect('/');
}
return $next($request);
}
}
The problem
Evan if I am not logged in I can still see the dashboard (the /dashboard route).
Shouldn't this line deal with the /dashboard route too?
Route::group(['prefix' => 'dashboard' , 'middleware' => ['checkSignedIn']], function() {
What am I doing wrong?

Change your routes like this:
// Dashboard routes
Route::group(['prefix' => 'dashboard', 'middleware' => ['checkSignedIn']], function() {
Route::get('/', [DashboardContoller::class, 'index'])->name('dashboard');
Route::get('/users', [UsersContoller::class, 'index']);
Route::get('/create-user', [UsersContoller::class, 'create']);
Route::get('/delete-user/{id}', [UsersContoller::class, 'delete']);
});

Related

How to use middleware for multiple type of admins in laravel?

I am using middleware in laravel. I have two middleware one is admin and second one is commissioner
Now in both middlewares some routes access to both middleware and some are not. Now what happen is i want personal routes of admin middleware not be accessed in commissioner middleware.
Here i have tried:-
//Admin Middleware Route
Route::group(["middleware" => ['admin']], function () {
Route::match(['get', 'post'], '/admin/users', 'AdminController#users');
});
//Commissioner Middleware Route
Route::group(["middleware" => ['commissioner']], function () {
//we can put later on these routes
});
// common middleware routes between commissioner and admin
Route::group(["middleware" => ['admin','commissioner']], function () {
Route::match(['get', 'post'], '/admin/dashboard', 'AdminController#dashboard');
Route::match(['get', 'post'], '/admin/profile', 'AdminController#profile');
});
Now when i access the AdminController#users route when i login through commissioner it is accessible but i want that route not be accessed in when commissioner login. but AdminController#dashboard and AdminController#profile should be accessible in both middleware
When admin login then type is : master
when commsioner login then type is : commissioner
// Commissioner Middleware
class Commissioner
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(empty(Session::has('adminSession'))){
return redirect()->action('AdminController#login')->with('flash_message_error', 'Please Login');
}
return $next($request);
}
}
// admin Middleware
class Admin
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(empty(Session::has('adminSession'))){
return redirect()->action('AdminController#login')->with('flash_message_error', 'Please Login');
}
return $next($request);
}
}
Please help me i am using laravel 5.2. Thanks in advnace :)
If I understand your problem correctly, you have one admin table which contains tow different types of admins: master and commissioner.
These two types of admins are both logined in by invoke AdminController#login method. You want to use middleware to check the types of the admin to protect your routes.
Below is my suggestions:
Create three different middlewares:
AdminAuth middleware (give it a name in Http/Kernel.php as "admin") for authentication checking for both master and commissioner.
Master middleware (give it a name in Http/Kernel.php as "master") check master type admin.
Commissioner middleware (give it a name in Http/Kernel.php as "commissioner") check commissioner type admin.
Middlewares:
class AdminAuth
{
public function handle($request, Closure $next)
{
if(!Session::has('adminSession')){
return redirect()->action('AdminController#login')->with('flash_message_error', 'Please Login');
}
return $next($request);
}
}
class Master
{
public function handle($request, Closure $next)
{
$admin = ... // Your code to retrived authenticated admin instance.
if($admin->type !== 'master') { // I assume you have a type field.
// return error here to indicate user is not a master
}
return $next($request);
}
}
class Commissioner
{
public function handle($request, Closure $next)
{
$admin = ... // Your code to retrived authenticated admin instance.
if($admin->type !== 'commissioner') { // I assume you have a type field.
// return error here to indicate user is not a commissioner
}
return $next($request);
}
}
Update your route like below:
Routes:
//Admin Middleware Route can only be accessed by master admin
Route::group(["middleware" => ['admin', 'master']], function () {
Route::match(['get', 'post'], '/admin/users', 'AdminController#users');
});
//Commissioner Middleware Route
Route::group(["middleware" => ['admin', 'commissioner']], function () {
//we can put later on these routes
});
// common middleware routes between commissioner and admin
Route::group(["middleware" => ['admin']], function () {
Route::match(['get', 'post'], '/admin/dashboard', 'AdminController#dashboard');
Route::match(['get', 'post'], '/admin/profile', 'AdminController#profile');
});
BTW, the middlewares are "AND" relationship. Say you have below declaration in your routes:
"middleware" => ['admin', 'commissioner']
This means the route can be accessed only when you passed both 'admin' and 'commissioner' checking.

Apply Middleware to all routes except `setup/*` in Laravel 5.4

I'm experimenting with Middleware in my Laravel application. I currently have it set up to run on every route for an authenticated user, however, I want it to ignore any requests that begin with the setup URI.
Here is what my CheckOnboarding middleware method looks like:
public function handle($request, Closure $next)
{
/**
* Check to see if the user has completed the onboarding, if not redirect.
* Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
*/
if ($request->user()->onboarding_complete == false && $request->path() != 'setup') {
return redirect('setup');
} else {
return $next($request);
}
}
This is being used in my routes like this:
Route::group(['middleware' => ['auth','checkOnboarding']], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
Route::group(['prefix' => 'setup'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
});
Now, if I go to /home or /account I get redirected to /setup as you would expect. This originally caused a redirect loop error hence why & $request->path() != 'setup' is in the Middleware.
I feel like this is a really clunky way of doing it, and obviously doesn't match anything after setup like the setup/settings route I have created.
Is there a better way to have this Middleware run on all routes for a user, but also set certain routes that should be exempt from this check?
There's nothing wrong with what you're doing, however, I would suggest splitting your route groups up instead i.e.:
Route::group(['middleware' => ['auth', 'checkOnboarding']], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
});
Route::group(['prefix' => 'setup', 'middleware' => 'auth'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
Alternatively, have a parent group for your auth:
Route::group(['middleware' => 'auth'], function () {
Route::group(['middleware' => 'checkOnboarding'], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
});
Route::group(['prefix' => 'setup'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
});
This will also mean you can remove the extra condition in your middleware:
/**
* Check to see if the user has completed the onboarding, if not redirect.
* Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
*/
return $request->user()->onboarding_complete ? $next($request) : redirect('setup');
Hope this helps!
You can utilize the Controller class for this with pretty spectacular results.
If you create a __construct function inside of HTTP/Controllers/Controller.php then you can declare middleware to run on every controller action and even declare exceptions as needed.
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
public function __construct(){
$this->middleware('auth',['except' => ['login','setup','setupSomethingElse']]);
}
}
Be careful not to put any of the standard index, store, update, destroy functions in the exception or you'll open up potential security issues.
Since Laravel 7.7 you can use excluded_middleware like this:
Route::group(['middleware' => ['auth','checkOnboarding']], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
Route::group([
'prefix' => 'setup',
'excluded_middleware' => ['checkOnboarding'],
], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
});
In Laravel 8.x you can also use the withoutMiddleware() method to exclude one or many route to a group middleware
Route::middleware('auth')->group(function () {
Route::get('/edit/{id}',[ProgramController::class, 'edit'])->name('edit');
Route::get('/public', [ProgramController::class, 'public'])
->name('public')->withoutMiddleware(['auth']);
});
Check also the official doc: Here
There are 2 ways to go over this problem
Try screening your routes in routes file web.php or api.php
skip routes in middleware
In case of global middleware (middleware that you want to run before all routes), you should go with skipping routes in middleware.
For example:
//add an array of routes to skip santize check
protected $openRoutes = [
'setup/*',
];
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(!in_array($request->path(), $this->openRoutes)){
//middleware code or call of function
}
return $next($request);
}
For other middleware, you can easily skip in routes file and group routes based on your middleware.
For example:
Route::group(['middleware' => 'checkOnboarding'], function () {
Route::get('/home', 'HomeController#index');
Route::get('/account', 'AccountController#index');
});
Route::group(['prefix' => 'setup'], function () {
Route::get('/', 'OnboardingController#index')->name('setup');
Route::post('/settings', 'SettingsController#store');
});
Routes on which you dont want the middleware to run , simply put them outside of the function:
//here register routes on which you dont want the middleware: checkOnboarding
Route::group(['middleware' => ['auth','checkOnboarding']], function () {
//routes on which you want the middleware
});

Change password Middleware not call

Hello in my project I am using the auto generated password from the admin side.And when the user try to login I am checking that user changed the password or not if password is not changes I want to redirect the user at the changepassword screen. I set changepasword middleware for it but middleware do not call the changepassword redirection Link.
changepasword middleware
use Closure;
use Auth;
class ChangePassword
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->isAutoPasswordChanged() )
{
return redirect('/change_password');
}
else
{
return redirect('/tests');
}
}
}
web.php
Route::group(['middleware' => 'auth', 'changepassword'], function () {
Route::resource('/tests', 'TestController');
Route::resource('/clients', 'ClientController');
});
Go to app\Http\Kernel.php
and add this to $routeMiddleware
'change_password' => \App\Http\Middleware\ChangePassword::class,
Then in your routes, replace the middle ware line with
Route::group(['middleware' => ['auth', 'changepassword']], function () {
And I also believe the logic written in ChangePassword is wrong...
It should be
if (!auth()->user()->isAutoPasswordChanged()) {
return redirect(route('auth.change.password.get'));
}
return $next($request);
First, please use the route() function instead of simple string... You will not have to change the url here, if you ever change the route from your web.php
Since you are already using the auth middleware, there is no need for you to do auth()->check().
Secondly, there should be a NOT in the condition. Because if the AutoPassword is NOT changed, only then redirect to the route, otherwise the use should be returned the next request and NOT redirected to /tests
Check registration of middleware app/Http/Kernel.php Doc
If you have a route middleware, compare the name provided in app/Http/Kernel.php
but my guess is:
'middleware' => 'auth', 'changepassword' should maybe changed in 'middleware' => ['auth', 'changepassword']
Debug if middleware itself is called

Laravel route Action not defined

I am having trouble with Laravel routes. I'm trying to redirect to a controller after some middleware in the routes. But there is always this error.
The error is:
InvalidArgumentException in UrlGenerator.php line 558: Action
App\Http\Controllers\DashboardController#index not defined.
The route code is:
Route::get('/dashboard', ['middleware' => 'auth', function() {
return Redirect::action('DashboardController#index', array('user' => \Auth::user()));
}]);
The controller:
class DashboardController extends Controller
{
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
return view('dashboard')->with('user', \Auth::user());
}
}
But the above code actually works (so I guess that the controller actually works):
Route::get('/testdashboard', [
'uses' => 'DashboardController#index'
]);
So what is the problem? What is a valid route action?
This is might a better way to do it, change from
Route::get('/dashboard', ['middleware' => 'auth', function() {
return Redirect::action('DashboardController#index',
array('user' => \Auth::user()));
}]);
to
Route::get('/', [
'middleware' => 'auth',
'uses' => 'DashboardController#index'
]);
This is rather a comment than a post, but I can't send it at this time. I don't undestand why do you pass parameter (\Auth:user()) to a method that doesn't require it (but it's correct when you do it for the View).
Anyways I suggest you to work on your Middleware
public function handle($request, Closure $next)
{
if (Auth::check()) {
return redirect(...);
} else {
return redirect(...);
}
}
Use this route in place of your route and upgrade your Laravel Project to Laravel 8:
Route::middleware(['auth:sanctum', 'verified'])->group(function () {
Route::get('/dashboard', 'DashboardController#index')->name('daskboard');
});

Roles with laravel 5, how to allow only admin access to some root

I follow this tutorial : https://www.youtube.com/watch?v=kmJYVhG6UzM Currently I can check in my blade if user is a admin or not like this:
{{ Auth::user()->roles->toArray()[0]['role'] }}
HI ADMIN
#endif
How can I make my route only available for admin user?
You need to create a middleware for your route.
Use: php artisan make:middleware AdminMiddleware.
You will find in your middleware folder a new file with this name.
Put your logic in your middleware, e.g.
public function handle($request, Closure $next)
{
if(Auth::check())
{
return $next($request);
}
else
{
return view('auth.login')->withErrors('You are not logged in');
}
}
Once you have done your logic in your middleware, you can either call it in the route or make the middleware apply to all routes.
If you want to add it to all routes, go to Kernel.php and add it to the $middleware array, e.g.
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'App\Http\Middleware\VerifyCsrfToken',
'App\Http\Middleware\AdminMiddleware',
];
If you want to add it to specific routes only, add it to the $routeMiddleware variable and add the alias to the route. E.g.
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
'admin' => 'App\Http\Middleware\AdminMiddleware',
];
You can then add it to a route, as a filter, e.g.
Route::get('admin/profile', ['middleware' => 'admin', function()
{
}]);
For additional info visit the docs:
http://laravel.com/docs/master/middleware
EDIT
An improvement on this would be to use variadic functions which was introduced in PHP 5.6
http://php.net/manual/en/migration56.new-features.php
Instead of having to make a middleware for each permission set you can do the following
PermissionMiddleware
namespace App\Http\Middleware;
use Closure;
use \App\Models\Role;
class PermissionMiddleware
{
// Pass parameters to this middleware
public function handle($request, Closure $next, ...$permitted_roles)
{
//Get a users role
$role = new Role;
$role_name = $role->getUserRoleByName();
foreach($permitted_roles as $permitted_role) {
if($permitted_role == $role_name) {
return $next($request);
}
}
return redirect()->back()->withErrors('You do not have the required permission');
}
}
Notice the ...$permitted_roles
Route::get('admin/profile', ['middleware' => 'PermissionMiddleware:Admin,Marketing', function()
{
}]);
You can now specify as many roles as required for one middleware rather than creating multiple by using middleware parameters
Docs
https://laravel.com/docs/5.3/middleware#middleware-parameters
Let's assume you have a column in your users table with isAdmin name which has a default value of 0 (false)
You can give special access using middleware in laravel like you give access to logged in users using auth middleware in laravel.
Now you need to create a middleware using the command :
php artisan make:middleware AdminMiddleware
In your Kernel.php you need to add this line to protected $routeMiddleware
'admin' => \App\Http\Middleware\AdminMiddleware::class,
In your middleware folder you have the AdminMiddleware file.
In that you need to put your logic
In this case this is how it might look like depending upon you
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RoleMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(Auth::user()->isAdmin == '1') // is an admin
{
return $next($request); // pass the admin
}
return redirect('/'); // not admin. redirect whereever you like
}
}
Now in your route you have to pass the url using this middleware
Here is how it might look like
Route::get('/iamanadmin', ['middleware' => 'admin', function() {
return view('iamanadmin');
}]);
use middleware and check for admin user.
Route::get('admin', ['middleware' => 'checkadmin', function()
{
}]);
now create middleware and validate admin user.

Categories