I'm trying to use auth middleware within my controller at construct method with except argument and it does not working.
I created my controller with artisan command and its contains methods like create, edit, show, etc.
here is my construct:
public function __construct()
{
$this->middleware('auth', ['except' => ['index', 'show']]);
}
if i visit some methods like edit and create when i'm not logged in middleware does not works and i can see the content. i also tried only instead of except, same result.
did you change Authenticate.php for auth middleware this function
public function handle($request, Closure $next)
{
}
I figure out that problem was with my addressing/
i had except index and show at my construct and i was trying to access the edit method with /controller/edit which is wrong, because controller assume the edit part as an index method argument, so i changed the url to /controller/1/edit and it's working.
it was my bad, cause i used Codeigniter for a long time, sometimes i still think that i'm working with Ci.
Related
So I created a middleware to limit the data a connected user has access to by adding global scopes depending on some informations:
public function handle(Request $request, Closure $next)
{
if (auth()->user()?->organization_id) {
User::addGlobalScope(new OrganizationScope(auth()->user()->organization));
}
return $next($request);
}
The middleware is added to the 'auth.group' middleware group in Kernel.php which is used in web.php:
Route::middleware(['auth.group'])->group(function () {
Route::resource('users', UserController::class);
});
Then in the controller, I would expect a user to get a 404 when trying to see a page of a user he has no rights to. But the $user is retrieved before the middleware applies the global scope!
public function show(User $user, Request $request) {
// dd($user); // <= This actually contains the User model! It shouldn't, of course.
// dd(User::find($user->id)); // <= null, as it should!
}
So, the dependency is apparently calculated before the middleware is applied. If I'm trying to move the middleware into the 'web' group in Kernel.php it's the same. And in the main $middleware array, the authenticated user's data is not available yet.
I found this discussion that seems to be on topic : https://github.com/laravel/framework/issues/44177 but the possible solutions (and Taylor's PR) seems to point to a solution in the controller itself. Not what I'm trying to do, or I can't see how to adapt it.
Before that I was applying the global scopes at the Model level, in the booted function (as shown in the docs). But I had lots of issues with that - namely, accessing a relationship from there to check what is allowed or not is problematic, as the relationship call will look for something in the Model itself, and said model is not ready (that's the point of the booted method, right...). For example, checking a relationship of the connected user on the User model has to be done with a direct query to the db, that will be ran every time the Model is called... Not good.
Anyway, I like the middleware approach as it is a clean way to deal with rights as well, I think. Any recommandation?
Not a recommendation, just my opinion.
This issue is just because of that Laravel allow you add middleware in controller constructor, and that's why it calculate before midddleware in your case.
I agree that middleware is a clean way to deal with auth, but i also think that you are not completely doing auth in your middleware, for example if you create a new route will you need to add something auth action into your new controller or just add auth middleware to route?
If does needs add something to controller, that means your auth middleware is just put some permissions info into global scope and you are doing the auth in controller which i think it's not right.
Controller should be only control the view logic, and you should do full auth in your auth middleware, once the request passed into your controller function that means user passed your auth.
For some example, if you auth permissions like below, you can just add auth middleware to new route without any action in your controller when you trying to create new route.
public function handle(Request $request, Closure $next)
{
if (auth()->user()->canView($request->route())) { // you should do full auth, not just add informations.
return $next($request);
}
else
abort(404);
}
I'm coming from conventional PHP background and trying to create my first big project in Laravel.
I usually user User/Role/Permission to manage user permissions in my applications. It works like follows:
User has many Roles
Role has many Permissions
to make things simple, I actually used the page names as permissions, so that I check the current page name against user permissions.
That was all easy in PHP, now I am trying to implement a similar approach in Laravel. I have User, Role, Permission models, and I check if user has permission using a method in User model as follows (inspired from a Laracasts tutorial):
public function permissions()
{
return $this->roles->map->permissions->flatten()->pluck('name')->unique();
}
And in my AuthServiceProvider I added the following code:
Gate::before(function ($user, $permission){
return $user->permissions()->contains($permission);
});
So if I add some permission (for example 'add_user') to the user, I can simply do the following in the route, and it works just fine:
Route::get('/test', function () {
return 'You are authorized';
})->name('add_user')->middleware('can:add_user');
Now since I have a lot of pages, I wouldn't like to pass specific permission name to the middleware, rather find a better and more generic way.
The only way I could come up with is to use the permission name same as the route name, and create a new middleware to take care of authorization.
So In my solution I added the following middleware class:
class BeforeMiddleware
{
public function handle($request, Closure $next)
{
$route_name = $request->route()->getName();
if(!Auth::user()->permissions()->contains($route_name)) {
throw new \Exception('Not Authorized');
}
return $next($request);
}
}
Added it to Kernel.php:
protected $routeMiddleware = [
'before' => \App\Http\Middleware\BeforeMiddleware::class,
...
];
And finally changed the route to be as follows:
Route::middleware(['before'])->group(function () {
Route::get('/test', function () {
return 'You are authorized';
})->name('add_user');
});
This way I don't actually have to pass the permission name when I check the permission, and directly get it from the route name.
I have many questions about my solution: is it really a good approach? Does it have any drawbacks? Is there a better approach?
Also I preferred to use AuthServiceProvider instead of the new middleware, but I couldn't retrieve the route name from ServiceProvider scope. Can I somehow use AuthServiceProvider for a similar case?
Sorry if I made the post somehow long, but I needed to be as clear as I could.
In my Laravel 6.0 app, I need to use a route with two parameters.
Route::get('view/{MyFirstModel}/{MySecondModel}', 'Mycontroller#view')
->middleware(['can:view,MyFirstModel,MySecondModel']);
I tried also with:
Route::get('view/{MyFirstModel}/{MySecondModel}', 'Mycontroller#view')
->middleware(['can:view,MyFirstModel|MySecondModel']);
In my Policy I have:
public function view(User $user, MyFirstModel $first, MySecondModel $second) {
var_dump("I enter");
}
However, it doesn't work, and the policy is never called. How can I pass two parameters in my route middleware?
I found my error.
Seems that I should use this sintax:
Route::get('view/{MyFirstModel}/{MySecondModel}', 'Mycontroller#view')->middleware('can:view,MyFirstModel,MySecondModel')
Without parentheses.
I'm in the process of upgrading a Laravel 4.2 app I inherited to Laravel 5.2. The app has multiple roles for logged in users that were handled with a before filter. Each controller has an array of functions and roles allowed for those functions:
public $actionFilter = [
'directories-create'=>['super','tsr'],
'directories-destroy'=>['super','tsr'],
'directories-edit'=>['super','tsr'],
'directories-directoryinfo'=>['super','tsr','admin'],
'directories-index'=>['super','tsr'],
'directories-store'=>['super','tsr'],
'directories-update'=>['super','tsr'],
];
then in the construct function it calls two beforeFilters that were in Controller.php
public function __construct()
{
$this->beforeFilter('#filterAuthorization');
$this->beforeFilter('#rerouteSite');
}
Controller.php had a public function filterAuthorization that checked if user's role had access to the route, and a public function rerouteSite that allowed user to stay on the same page but switch between accounts (for example, for a support rep).
I've spent a fair amount of time reading the manual, Googling and reading various tutorials, but I'm still unclear how to get my route-role array connected to the auth middleware. The Laravel docs provide syntax but not the context and the examples I've read either take a different approach or have a different usecase from mine.
I tried leaving the filter functions in Controller.php and calling them like this in the construct:
public function __construct()
{
$this->middleware('#filterAuthorization');
$this->middleware('#rerouteSite');
}
I get an error message: "Class #filterAuthorization does not exist"
I tried putting those functions in app\Http\Middleware\Authenticate, but I get the same error message: "Class #filterAuthorization does not exist"
I followed the steps on Matt Stauffer's blog here (https://mattstauffer.com/blog/laravel-5.0-middleware-filter-style/) and here (https://mattstauffer.com/blog/passing-parameters-to-middleware-in-laravel-5.1/) and on Nwanze Franklin's post here (https://dev.to/franko4don/deep-dive-into-middlewares-in-laravel-doo) as follows.
Create two new middleware files with Artisan
php artisan make:middleware FilterAuthorization
php artisan make:middleware RerouteSite
Edit the new middleware files with the functions from the old Controller.php
Register the new middleware in App\Http\Kernel
protected $routeMiddleware = [
'filterauth' => \Illuminate\Routing\Middleware\FilterAuthorization::class,
'reroutesite' => \Illuminate\Routing\Middleware\RerouteSite::class,
];
Edit the public function __contstruct() in the Controllers than need filtering
public function __construct()
{
$this->middleware('FilterAuthorization');
$this->middleware('RerouteSite');
}
Run
composer dump-autoload
php artisan clear-compiled
php artisan optimize
and I still get the same error:
Class FilterAuthorization does not exist
I'm sure there's a simple way to put this together without rewriting the whole role authorization system. Can someone point me in the right direction?
The kernel registration needs to reference the correct file locations as follows:
'filterauth' => \App\Http\Middleware\FilterAuthorization::class,
'reroutesite' => \App\Http\Middleware\RerouteSite::class,
And the controller boot should use the aliases rather than the class names:
public function __construct()
{
$this->middleware('filterauth');
$this->middleware('reroutesite');
}
Then Laravel can find the custom middleware.
I'm trying specify which functions are able to each user but the problem is when I pass more than one authenticated user to middleware in my controller.
Here are my controller. I'm trying allow just one method for the users but at that piece of code, the second line don't work out. Just show a response for not authorized (401).
public function __construct()
{
$this->middleware('auth:admin');
$this->middleware('auth:web')->only('listAll');
}
Someone have some idea how to solve it ?
I tried use:
public function __construct()
{
$this->middleware('auth:admin')->except('listAll');
$this->middleware('auth:web')->only('listAll');
}
Works but it means that will be allowed for all users...