I am trying to pass multiple arguments to my policy method. I am calling the policy as a middleware on my route. In my policy I need the authenticated user, the target group and the invitee. The authenticated user gets passed automatically by Laravel.
I wish to call the policy like this so it matches my other routes, but this always returns a 403 forbidden. I believe this happens because the middleware doesn't know which policy to use.
Route::post('/{group}/invite/{invitee}', [GroupInvitationController::class, 'store'])
->middleware('can:store,group,invitee');
If I use this line in my controller method instead of calling it from the route using middleware it works perfectly.
$this->authorize('store', [GroupInvitation::class, $group, $invitee]);
My policy:
class GroupInvitationPolicy
{
public function store(User $user, Group $group, User $invitee) {
return $user->isGroupAdminOf($group) || (
$group->users->contains($invitee)
? Response::allow()
: Response::deny('User is already part of group')
);
}
}
Is there a way to call the policy from the route middleware while also providing the policy with the correct parameters?
Related
Im using Laravel 5.8 and I got the following situation:
I have a simple form with a button which sends a delete request to a route. The form works like this: When the button is pressed, the form's action redirects me to the URL localhost/delete/4 where 4 is the id of the entry in the database, and there the route kicks in and the controller deletes my entry.
However, unauthenticated users do not have access to the form's button, and the route is protected by the middleware 'auth'.
But, if I, as an unauthenticated user, type in the adress bar localhost/delete/4, I get a method unsupported error, which is expected because I send a get request to a delete type route.
But my question is why do I get this error at all? Since the route is protected by the middleware against unauthenticated users, why does the request reach the route since it should be blocked by the middleware?
Below you got the route:
Route::delete('/delete/{id}', ['uses' => 'LibraryController#getLibraryDelete', 'middleware' => 'auth']);
Oh, as a side note, if a change the route to receive get requests, and try again, the middleware works fine
The route is checked first before going to the middleware and controller...
So if the route was not found actually the script doesn't know which middleware or controller to go to...
--
Here is a good use case for example someone want to define the following routes
Route::get('/question/{id}', 'QuestionController#view');
GET /question/1 is public for all users and returns the question itself (read only)
but
Route::patch('/question/{id}', 'QuestionController#edit')->middleware('auth');
PATCH /question/1 is only authenticated user can edit question...
So it's acceptable that different methods can have different middlewares or no middlewares for the same route...
And that some methods are not defined/allowed
--
The method is unsupported because your defined route is for deletes only as in ::delete method you used
Delete request is either a HTTP POST request with a query called "_method" and value "delete" or in supported browser an HTTP DELETE request
When the user type the url manually in their address bar it's a GET request which can be handled by this route method ::get
Available routing methods from latest documentation: (https://laravel.com/docs/5.8/routing)
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
Plus special route ::any which accepts any method
In laravel if the user reach a url that is defined but with a Method that's not defined in routes you get this "Method unsupported"
The method unsupported error is irrelevant to auth middleware in this case ... it's just about routing
I have a String with a middleware rule (like in routes):
$middleware = "can:index,App\Models\Order";
Is there any possiblity to check if a given user has access with this middleware rule?
That is how laravel policies define authorization rules in middleware. See here: https://laravel.com/docs/5.5/authorization#via-middleware
Create a policy class for the model you are authorizing then register the policy to model.
It is possible to see if a given user is authorized to carry out a certain action in many different ways. One is by using the middleware in your question and attaching it to a route or group. Here are some other ways:
Using the can method on the User object. The can method is inherited from the Authorizable trait (So it's not limited just to users):
if ($user->can('index', 'App\Models\Order')) {
// User is allowed to index orders.
}
Using the authorize method on a controller. The authorize method is inherited from the AuthorizesRequests trait (So this is not limited to just controller). This method throws an exception if authorization fails:
$this->authorize('index', 'App\Models\Order');
In a view, it is possible to use the #can Blade directive to see if a user is authorized to carry out the given action:
#can('index', 'App\Models\Order')
This user can index orders.
#endcan
If you have that specific string, you could do a little bit of manipulation to extract the bits you need and then pass it to one of the above methods:
$middleware = "can:index,App\Models\Order";
list($rule, $parameters) = explode(':', $middleware);
list($ability, $model) = explode(',', $parameters);
if ($user->can($ability, $model)) {
// User can index orders.
}
Of course it would be wise to do more error checking etc.
Assume the following routes in a Laravel 5.5 API:
// custom routes for THIS user (the user making the request)
Route::get('/user', 'UserController#show');
Route::get('/user/edit', 'UserController#edit');
// register CRUDdy resource routes for users
Route::resource('users', 'UserController');
Let's use edit as the example:
public function edit(User $user)
{
...
}
As you can see, the edit route contains a type-hinted $user parameter. This works just fine for the users/13/edit route, as expected. However, I'd to configure the route /user/edit to pass along the $request->user() user object to the function. I don't want to check for the user object in the actual edit method as that could interfere with other validation (for instance, I don't want to default to the current user if someone passes a non-existent user ID, I want to return an error in that case).
In short: how can I register a route to first create a parameter, then pass it to the given controller method? My first thought was to use a closure:
Route::get('/user/edit', function(Request $request){
$user = $request->user();
});
But once inside the closure, I'm not certain how to then carry the request forward to the appropriate controller method.
Instead of a closure, you could make a new controller method that calls edit with the current user.
Let's say your route is this:
Route::get('/user/edit', 'UserController#editSelf');
Then in your controller:
public function editSelf(Request $request)
{
$this->edit($request->user());
}
I am today in bit confusion about my website security and some extra code that is written to make the website secure. Below are 2 locations where security is applied.
Inside Route Config, To secure the route, I have used Middleware to check the user role.
Route::group(['middleware' => ['web', 'SuperAdmin', 'auth']], function () {
Route::get('/Create-Department', 'DepartmentController#CreateDepartment');
});
I mentioned 2 Middlewares.
Auth Middleware : This is for authentication.
SuperAdmin Middleware: This is for Authorization.
Second location is Request class. Here is the code. In authorize method, again same thing is being checked as already done in route
class DepartmentRequest extends Request
{
public function authorize()
{
if(\Auth::user() == null) {
return false;
}
if(\Auth::user()->isSuperAdmin()) {
return true;
}
return false;
}
public function rules()
{
return [
'Department' => 'required',
];
}
}
Question: Should I remove check in Request class? Is that an unwanted validation to secure the request ? As route.config is already doing the job.
What's the use of authorize method? I meant, I am using Request class to validate form inputs. Should I always return true from authorize method?
yes, you should remove that checks in the Request class: if you're already doing that checks in your middleware you should not repeat them
When you specify this:
Route::group(['middleware' => ['web', 'SuperAdmin']], function () {
Route::get('/Create-Department', 'DepartmentController#CreateDepartment');
});
You're telling laravel that, when it finds a /Create-Department route, it should trigger the handle method of these middleware: ['web', 'SuperAdmin'], before the request is sent to the DepartmentController
So, if you check for authentication and authorization in the middlewares, when the request will get to your controller you're sure that it has satisfied all the middleware it went through
Regarding the purpose of the authorize method: the authorize method is usually used to authorize the actual request basing on some policy you'd like to respect. For example, if you have a request to edit a Post model, in the authorize method you'd check that the specific user trying to edit the post has the permissions to do it (for example being the author of the post )
EDIT
Even if you want to use a middleware for your authorization, it's fine. Anyhow, usually the authorize method within form requests is used to do authorization checks on the specific request.
For instance check this example from the docs :
public function authorize()
{
$postId = $this->route('post');
//here the authorization to edit the post is checked through the Gate facade
return Gate::allows('update', Post::findOrFail($postId));
}
In conclusion: if you're doing your authentication and authorization tasks in middlewares, you don't need to repeat them in the authorize method, but keep in mind that the native purpose of the method is to authorize the specific request
I'm building an API for user and admin.
Got stuck at edit user profile routing.
on admin route i use Route::resource('user', 'UserController')
on user route i use Route::get('profile', 'UserController#show')
At the show method Laravel default has
public function show($id)
{
}
the different between them is on admin I can use /id but on user i check their token from middleware and merge the request to get their user_id so there is no need for the API to use profile/{id}.
The question is how can I use the same method but there is an argument to fill and the route still /profile?
One of my solution is :
public function show($id){
if ($request->has('user_id')):
$id = $request->query('user_id');
endif;
}
It working but when i read the code, it's really redundant always checking it and replace the id.
Just place the request object as a parameter in your controller and get the input from the request object when you use your user route.
Thanks