Laravel middleware with prefix not working - laravel

Currently I am working with a new project with laravel. For this project I need to add prefix for a group of routes and need to add the prefix by using middleware. The middleware is -
public function handle($request, Closure $next)
{
$segments = $request->segments();
if( $request->is('admin/*') ){
return $next($request);
}
array_unshift($segments,'admin');
return redirect()->to(implode('/',$segments));
}
And my routes/web.php file is-
Route::group(['middleware' => 'admin','prefix' => 'admin'],function(){
Route::get('segments',function(){
return request()->segments();
});
});
But unfortunately this is not working for me. The middleware not force redirect me if I don't add admin/ prefix manually. But if I remove 'prefix' => 'admin' from the route group then it works. How can I solve this problem?
Sorry, for my bad English.

This approach will not work for you because you're only applying the middleware to the admin/* routes.
If you want to redirect a user from any not admin/* routes to admin ones, you could do something like this instead. Add this route to the end of the web.php:
Route::get('{param1?}/{param2?}/{param3?}', 'RedirectController#redirectoToAdmin');
Create RedirectController controller and do this:
public function redirectoToAdmin()
{
return redirect('admin/' . request()->path());
}
Also, remove the admin middleware from the route group you've shown.

Related

Laravel 6 perform role based routing

I have a route group with different routes. I want to have different role levels access without changing the URL of the application.
For example I want to have /admin as the route and then I want to allow or disallow users based on their roles. Basically, I want every user to be able to see the same page but with different menu options(I know how to do this) but also secure the links from direct access.
Is there a nice way to achieve that without the need of using different middlewares seperately on each route? Since there doesn't seem to be a way to retrieve the $request variable inside the web.php file but only inside a controller. I'm using the sentinel package for auth.
Some sample code of my web.php:
Route::group(
['prefix' => 'admin', 'middleware' => 'customer', 'as' => 'admin.'],
function () {
// Ad list
Route::get('getMyAnnonsList', 'Admin\BackEndController#getMyAdList')->name('getMyAdList');
}
);
Great answer by #lagbox. This is what I did in the end. Very elegant.
web.php:
Route::group(['prefix' => 'admin', 'as' => 'admin.'], function () {
Route::middleware('admin:admin,user')->group(function(){
Route::get('getMyAnnonsList', 'Admin\BackEndController#getMyAdList')->name('getMyAdList');
});
});
middleware:
public function handle($request, Closure $next, ...$roles)
{
if (!Sentinel::check())
return redirect('admin/signin')->with('info', 'You must be logged in!');
foreach($roles as $role)
if($role == Sentinel::getUser()->roles[0]->slug)
return $next($request);
return redirect()->back();
}
I had already answered something like this before, should be working the same still.
You can create a middleware that can be applied to your group. In that middleware it is asking the route itself for the specific roles to check.
How to assign two middleware to the same group of routes. Laravel
Example of middleware:
class CheckMiddleware
{
public function handle($request, Closure $next)
{
$roles = $request->route()->getAction('roles', []);
foreach ((array) $roles as $role) {
// if the user has this role, let them pass through
if (...) {
return $next($request);
}
}
// user is not one of the matching 'roles'
return redirect('/');
}
}
Example route definition:
Route::middleware('rolescheck')->group(function () {
Route::get('something', ['uses' => 'SomeController#method', 'roles' => [...]])->name(...);
});
You can apply this arbitrary data at the group level, the individual route level or both, as all routes are individually registered; groups just allow for cascading of configuration.
You could also have this middleware take parameters, and just merge them with the arbitrary roles, then it is a dual purpose middleware:
public function handle($request, $next, ...$roles)
{
$roles = array_merge($roles, $request->route()->getAction('roles', []));
...
}
Route::middleware('rolescheck:admin,staff')->group(...);
You can use Laravel Gate And Policies
You can define the gate inside the App > Providers > AuthServiceProvider
and you can also create policies per CRUD. just see info in php artisan help make:policy. This will create a folder in your app called policies you can define the who can access it.
In your controller you can do is this: (this is a gate middleware)
I define the gate first:
Gate::define('check', function ($user, $request) {
return $user->roles->contains('name', $request) || $user->roles->contains('name', 'root');
});
then I initialise it in the controller
abort_if(Gate::denies('check', 'admin only'), 403);
This will throw 403 error if the user don't have access on that role. It will check if the user has admin only role. If it doesn't have it will throw the error
In your view if you want to disable anchor links you can do like this:
#can('check', 'admin only')
dashboard
#endcan
EDIT:
Controller
public function index() {
abort_if(Gate::denies('check', 'admin only'), 403);
// Your Code...
}

Auth::user() is null in new route

i'm using laravel 6 and have 2 route in my app; index and dashboard.
My routes/web is:
Auth::routes();
Route::middleware(['auth'])->group(function () {
Route::get('/index', 'todoApp\TodoController#index')->name('index');
Route::get('/dashboard', 'todoApp\Dashboard#dashboard')->name('dashboard');
});
i added dashboard route recently.
Auth::user() is null when i dump it in dashboard route but doesn't in index. What's the
Your Controller is instantiated before the middleware stack has ran; this is how Laravel can know what middleware you have set via the constructor. Because of this you will not have access to the authenticated user or sessions at this point. Ex:
public function __construct()
{
$this->user = Auth::user(); // will always be null
}
If you need to assign such a variable or access this type of information you would need to use a controller middleware which will run in the stack after the StartSession middleware:
public function __construct()
{
$this->middleware(function ($request, $next) {
// this is getting executed later after the other middleware has ran
$this->user = Auth::user();
return $next($request);
});
}
When the dashboard method is called, the middleware stack has already passed the Request all the way through to the end of the stack so all the middleware needed for Auth to be functioning and available has already ran at that point which is why you have access to Auth::user() there.
I think that this has something to do with the 'web' middleware. If you take a look into the Kernel.php (In app\Http) you will find the web middleware group.
This will show you that it actually calls a middleware called StartSession. Based on your route file (where web is not included as a middleware) I would think that you don't have a session in your Controller and there for no access to it.
I don't quite understand why this only happens in your /dashboard route, because the issue should also be in your /index route (unless you added the web middleware somewhere in your TodoController).
I think that this should do the trick:
Route::middleware(['web', 'auth'])->group(function () {
Route::get('/index', 'todoApp\TodoController#index')->name('index');
Route::get('/dashboard', 'todoApp\Dashboard#dashboard')->name('dashboard');
});
If you fire php artisan make:auth command.
It's doesn't matter where you define because of it's only define auth route
Route::middleware(['auth'])->group(function () {
Route::get('/index', 'todoApp\TodoController#index')->name('index');
Route::get('/dashboard', 'todoApp\Dashboard#dashboard')->name('dashboard');
});
Auth::routes();

Route group with multiple middleware checks

In my laravel project i have made my own roles and added them to middleware so i can use auth.admin or auth.superadmin to protect specific routes.
I have a Route::Group for my super admin role, a Route::Group for my admin role and a Route:Group for the standard auth check.
Now i have a specific Route that has to be accessed by the superadmin and the admin.
When i place the route in the admin group OR the superadmin group it works. But when i try to place it in both or make a route group where i check for both roles it doesnt.
Then i tried making a Route::Group like this:
Route::group(['middleware' => ['auth','auth.admin', 'auth.superadmin']], function() {
Route::resource('user', 'UserController', ['except' => ['show']]);
});
I was thought this would fix my problem but it didnt.
How can i make a Route Group where only admins and superadmins can acces the route.
Rewrite your middleware to use a setup like this:
Route::get('/home', ['middleware' => 'roles:admin,superadmin', function () {
echo '/home';
}]);
And then using the ... operator you can easily check the parameter $roles as an array:
// YourMiddleware.php
public function handle($request, Closure $next, ...$roles)

Apply Auth Middleware to All Laravel Routes

What is the correct way to authenticate all routes except login and register when I apply auth middleware in all controllers? Is there a way to apply auth middleware in one place and exclude login, register routes?
You can group all your authenticated routes like following, laravel provides a default middleware for auth and guest users
Route::group(['middleware' => ['auth']], function () {
Route::get('home', 'HomeController#index');
Route::post('save-user', 'UserController#saveUser');
Route::put('edit-user', 'UserController#editUser');
});
The above route names are just made up, please follow a proper naming convention for your routes and controllers. Also read about middlewares over here and about routing over here
you can apply middlewares in the routes.php file, what you need to do is to put all your routes on a group, and add the middleware 'auth' ( except the Auth::routes() which are already configured), for example :
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// Uses first & second Middleware
});
Route::get('user/profile', function () {
// Uses first & second Middleware
});
});
more information can be found in the docs: https://laravel.com/docs/5.7/routing#route-group-middleware
You can add middleware to your whole web.php route file by adding the middleware to your routes mapping in RouteServiceProvider.
Go to app/Providers/RouteServiceProvider.php and in mapWebRoutes(), change middleware('web') to middleware(['web', 'auth']):
protected function mapWebRoutes()
{
Route::middleware(['web', 'auth'])
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
This is (not?) totally unrelated but here's an example of a clean way to handle a lot of route files instead of throwing all your routes into a single web.php file:
Create a new method mapAdminRoutes():
protected function mapAdminRoutes()
{
Route::middleware(['web', 'auth:admin'])
->namespace('App\Http\Controllers\Admin')
->name('admin.')
->group(base_path('routes/admin.php'));
}
Map it:
public function map()
{
$this->mapWebRoutes();
$this->mapAdminRoutes(); // <-- add this
...
}
Create an admin.php file in your routes folder, then create your routes for Admin:
<?php
use Illuminate\Support\Facades\Route;
// This route's name will be 'admin.dashboard'
Route::get('dashboard', 'DashboardController#dashboard')->name('dashboard');
// This route's name will be 'admin.example'
Route::get('example', 'ExampleController#example')->name('example');
...
Now you can configure everything in 1 place, like prefix, name, middleware and namespace.
Check php artisan route:list to see the results :)

Passing variables into middleware Laravel

I am using wildcard subdomains and want to pass the $element variable into the middleware whitelabel so I can check the subdomain and respond accordingly.
Route::group(['domain' => '{element}.website.co', 'middleware' => 'whitelabel'], function() {
Route::get('/', 'AuthController#getLogin');
Route::post('/', 'AuthController#postLogin');
});
How would I use the value of element within the middleware?
Firstly, (unless you already have done) you'll need to add the following to your:
Route::pattern('element', '[a-z0-9.]+');
You can add it to the boot() method of your AppServiceProvider.
Then to access it in your middleware you would have something like:
public function handle($request, Closure $next)
{
$domain = $request->route('element');
return $next($request);
}
Hope this helps!

Resources