I have a route resource group that can only be accessible by one of 2 middleware rules. I have registered them both and they both work independently if I test them both out alone, but when I have them together they don't work
I have tried running them both as either an "or" statement (which means the middleware works as intended) but this means that anyone not logged in can also access the routes for some reason. If I use a comma to separate the middleware, it's blocked for everyone. I know both middleware works ok as they do work if I try them independently. I am using the below code
Route::group(['middleware' => ['IsAdmin' or 'IsPatreon']], function(){
Route::resource('patreon', 'patreonGalleryController', ['names'=>[
'index'=>'patreonGallery.index',
'create'=>'patreonGallery.create',
'store'=>'patreonGallery.store',
'edit'=>'patreonGallery.edit',
'show'=>'patreonGallery.show',
'destroy'=>'patreonGallery.destroy',
]]);
});
How can I set it so that only either admin or patreon uses can see the paths?
Two middlewares are working separately.
IsAdmin is checking that user is admin
IsPatreon is checking that user is patreon...
You cannot merge these 2 middlewares by OR Operator
Probably you need to create new middelware, something like
IsAdminOrPatreon and do you checks inside of that middleware and assing that middleware to your Group..
Or you can try with middleware parameters, for example
Route::group(['middleware' => ['checkRoles:admin,patreon']], function(){
Route::resource('patreon', 'patreonGalleryController', ['names'=>[
'index'=>'patreonGallery.index',
'create'=>'patreonGallery.create',
'store'=>'patreonGallery.store',
'edit'=>'patreonGallery.edit',
'show'=>'patreonGallery.show',
'destroy'=>'patreonGallery.destroy',
]]);
});
And in you checkRoles middleware get the admin and patreaon roles like this:
public function handle($request, Closure $next) {
// will contain ['role1', 'role2']
$allowedRoles = array_slice(func_get_args(), 2);
// here you can loop and check your roles
}
Note! If you pass 'checkRoles:admin,patreon' you will get
array(admin,patreon)
If you pass 'checkRoles:admin' you will get
array(admin)
you can't use or condition inside middleware array. middleware array always return and condition. you can specify the user role inside your middleware.
gist sample role middleware
https://gist.github.com/ivanhoe011/931417be3e36b3f06e994bfe5cd004f9
You do something like this in your controller.
public function __construct()
{
return ($this->middleware('IsAdmin')) || $this->middleware('IsPatreon');
}
Each route on this controller will be authenticated by any one of middleware.
Related
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.
I started creating a REST API using the lumen framework and wanted to set up a particular behaviour for my GET /user route. Behaviour is the following:
If the request come from an authenticated user (using auth middleware), the method getAllFields from UserController is called and return all the data from the user
If it's not the case, the method get from UserController is called and return some of the data from the user
It seems logic to me to just write it like that in my web.php using a simple middleware:
<?php
$router->group(['middleware' => 'auth'], function () use ($router) {
$router->get('/user/{id}', [
'uses' => 'UserController#getAllFields'
]);
});
$router->get('/user/{id}', [
'uses' => 'UserController#get'
]);
But for some reason, even if the middleware is correct, I always get the response of the second route declaration (that call get()). I precise that if I remove the second route declaration, the one in the middleware work as expected.
Have someone an idea how I can achieve something similar that work?
Router will check if your request matches to any declared route. Middleware will run AFTER that match, so You cannot just return to router and try to find another match.
To fallow Laravel and Routes pattern - You should have single route that will point to method inside controller. Then inside that You can check if user is logged or not and execute getAllFields() from that controller. It will be not much to rewrite since You are currently using UserController in both routes anyway.
web.php
$router->get('/user/{id}', 'UserController#get');
UserController.php
public function get()
{
return auth()->check() ? YourMethodForLogged() : YourMethodForNotLogged();
}
Or if there is not much logic You can keep this in single method.
Also it is good idea to fallow Laravels REST standards (so use show instead of get, "users" instead of "user" etc - read more https://laravel.com/docs/7.x/controllers)
web.php
$router->get('/users/{user}', 'UserController#show');
UserController.php
public function show(User $user)
{
if (auth()->check()) {
//
} else {
//
}
}
To summary - for your needs use Auth inside controller instead of middleware.
To check if user is logged You can use Facade Auth::check() or helper auth()->check(), or opposite Auth::guest() or auth()->guest().
If you are actually using Lumen instead of full Laravel then there is not auth helper by default (You can make own or use package like lumen-helpers) or just keep it simple and use just Facades instead (if You have then enabled in Lumen).
Read more https://laravel.com/docs/7.x/authentication and https://lumen.laravel.com/docs/7.x/authentication
This pattern is against the idea of Laravel's routing. Each route should be defined once.
You can define your route without auth middleware enabled and then define your logic in the controller.
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 :)
so i have my routes defined within a global route group and inside i have two others groups, on with middleware 'employee' and one with middleware 'admin' and each one have their own controllers.
The two groups have a route index with the same url " /document " but each executes a different controller.
My problem is the index route that the last defined group always overrides the previous one.
The wanted behaviour is that if employee middleware is valid, execute the empController#index action, else if the admin controller is valid, execute the adminController#index action while keeping the same url for both, and i must keep each route in a seperate group, and the two routes must have the same url.
How can i acheive that? Thanks
here are the two conflicting routes
Route::group(['as' => 'admin.', 'middleware' => ['admin']], function() { Route::get('/document', 'Admin\DocumentController#index')->name('document.index'); .... });
Route::group(['middleware' => ['employee']], function() { Route::get('/document', 'DocumentController#index')->name('document.index'); .... });
It sounds like you want to perform different actions based on whether the request is coming from an employee or an admin. My suggestion would be to have one url, one route and one controller function for this.
Within the controller function (or in middleware that attaches its findings to the request if you wanted to do that), have logic to call one method or another.
A pseudo example in PersonController.php:
if(\Auth::user()->isAdmin()) {
// return an admin view or perform an admin function
else {
// return an employee view or perform an employee function
}
Where the isAdmin() function is something you've defined in a model like User.php.
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.