I know, how to use Laravel policies and everything works, but I am stuck with create(...) method.
My app is a training diary for ski racers. Each racer can manage (view, add, edit..) his own diary. Each trainer can manage his own diary, diary of all racers but not other trainers. I use native Laravel Policies and it works great during *update(...) and delete(...) method, but not create(...).
TrainingRecordPolicy.php:
public function update(User $user, TrainingRecord $record)
{
if ($user->id == $record->user->id) {
return true;
}
if ($user->isTrainer() && $record->user->isRacer()) {
return true;
}
return false;
}
I'm using it in controllers like $this->authorize('update', $record). But if I want to check, if user can create new record into others user diary, I have no idea how to deal with it.
This doesn't work $this->authorize('create', TrainingRecord::class, $diaryOwner) and if I use $this->authorize('createDiaryRecord', $diaryOwner) it calls method inside UserPolicy.
How do I send $diaryOwner as extra parameter to create() method within the policy?
Note: $diaryOwner is retrieved from the route parameter user in route with signature /diary/{user}
You can access the $diaryOwner within the policy using request() helper.
public function create(User $user)
{
$diaryOwner = request()->user; // because route is defined as /diary/{user}
}
There may be a problem because:
When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the route parameters.
So instead of using request()->user use request()->route()->parameter('user').
Also if you are using \Illuminate\Routing\Middleware\SubstituteBindings::class you will get User instance.
Related
I'm using laravel basic policy system to protect unauthorized user from update post. For Example User have Id 1 and In posts Table User_id is also 1.
Now in $this->authorize('update',$post); way I can pass only one variable $post to authenticate. while in can method I can also use $user variable $user->can('update',$post) for authorize.
Here is code:
In PostPolicy.php :
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
In AuthServiceProvider.php :
protected $policies = [
Post::class => PostPolicy::class
]
In Controller Authorize way :
public function update(Request $request, $id)
{
$post=Post::find(1);
$user=User::find(1);
$this->authorize('update',$post);
return 'Hello Everything Access For You ';
}
Using can method in Controller :
public function update(Request $request, $id)
{
$post=Post::find(1);
$user=User::find(1);
if($user->can('update',$post)){
return 'Your are allowed';
}
else
{
return 'Your are Not allowed';
}
}
Is I'm right for these two functions. Is there any Difference. Which method I have to use. Thanks in Advance.
If you are using any of authorize() or can(), the purpose is to validate if user is authorized to do certain tasks.
But :
In case of authorize(), if it fails(returns false from policy method), the authorize method will throw an Illuminate\Auth\Access\AuthorizationException, which the default Laravel exception handler will convert to an HTTP response with a 403
In case of can(), it like just a basic method to check if user is authorized or not and then you need to handle the rest on your own. Like what to do if its unauthorized.
Considering above factors, I would say $this->authorize('update',$post); is easier to be used in controller.
Check more about the same in documentation
Also you can do :
$request->user()->can() if you want to check authorization of current requesting user.
Update :
authorize() is designed to authorize current user who is logged in, where laravel passes current user automatically to policy.
Whereas you can use can on any user instance.
$request->user()->can() if you want to check authorization of current requesting user.
$user = $user::find($id); $user->can(...) if its any other user
$this->authorize() checks if the current user is authorized. $user->can() checks if the user in $user is authorized. Both rely on the same underlying policy to make the decision.
The main difference between the two is that $this->authorize() throws an exception if the current user isn't authorized (as it's intended for use within a controller), whereas $user->can() just returns true/false.
If you want a controller to act as if it's doing $this->authorize() for a different user than the current one, you can do this:
// where "123" is the user you want to check against
$user = App\User::find(123);
if(!$user->can('update', $post) {
throw new \Illuminate\Auth\Access\AuthorizationException;
}
That said, this is rarely what you want to do - it doesn't (usually) make much sense to determine if the current user can do something based on another user's permissions.
Let's say we have action in the policy for our model that can return false in bunch of different scenarios:
class PostPolicy
{
public function publish(User $user, Post $post)
{
if ($post->user_id !== $user->id) {
return false;
}
return $post->show_at->lessThan(now());
}
}
As you can see, we are denying this user his publishing rights in two cases: if it's not his post or if this post was prepared in advance for some future date that is not yet due.
How can I provide some context as to why authorization failed? Was it because we are not the owner or was it because it's not time yet for this post to be published?
$user->can('publish', $post); // if this returns false we don't know
// what caused authorization to fail.
It looks that Laravel policies by design doesn't have any way of doing that. But I am curious as to what workarounds there can possibly be so that we can have authorization logic (no matter how intricate) in one place (model's policy) and also get some context (i.e., custom error codes) when authorization fails.
Any ideas?
In case any one needed,
apart from above accepted answer, in Laravel 7+, Gate can provide reasons for denial,
Reference: https://laravel.com/docs/7.x/authorization#gate-responses
Gate::authorize() calls will throw reason along with the exception with message provided in the Response::deny(<message>) call, the exception itself will be an Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
$user->can() or Gate::allows() will give boolean
Gate::inspect() will give full response
Given that you will now return a Gate Response object instead of boolean, Laravel will help on returning suitable response stated above.
<?php
use Illuminate\Auth\Access\Response;
Originally, you only return a boolean
<?php
class PostPolicy
{
public function publish(User $user, Post $post)
{
return $post->user_id !== $user->id;
}
}
By using Gate Response, you can now provide a reason
<?php
use Illuminate\Auth\Access\Response;
class PostPolicy
{
public function publish(User $user, Post $post)
{
return $post->user_id === $user->id
? Response::allow()
: Response::deny('You are not the author of the post.');
}
}
If in controller you are using the policy like :
<?php
$this->authorize('publish', Post::class)
Then laravel is going to have 403 HTTPResponse error.
What you should do is, one policy method should just be checking one validation case.
For example update your policy :
<?php
// Check if post he is trying to publish is his own
public function publishOwnPost(){...}
// Check if post is for future purpose
public function publicFuturePost(){...}
Then in controller do :
<?php
if(!$user->can('publishOwnPost', $post)){
// Return custom error view for case 1
return response()->view('errors.publishOwnPostError', $data, 403);
}
if(!$user->can('publishFuturePost', $post)){
// Return custom error view for case 2
return response()->view('errors.publishFuturePostError', $data, 403);
}
// Do further processing
What I ended up doing was splitting some of the responsibilities between model and it's policy.
Policy ended up responsible for making sure user has the right to do a specific action. Nothing more or less:
class PostPolicy
{
public function publish(User $user, Post $post)
{
return $post->user_id !== $user->id;
}
}
Model on the other hand must have logic to check whether or not certain action can be performed with it:
class Post extends Model
{
...
public function isPublishable()
{
return $this->show_at->lessThan(now());
}
...
}
Therefore each post instance now can tell us whether or not it can be published. Lastly my Post::publishBy(User $user) action will include authorizing user for this action first and checking if this post can be published separately so that we can determine specific reason as to why publishing failed.
I feel like this design suits better, leaving Laravel policies to do only what they are supposed to be doing (authorizing user actions) and requiring models to be responsible for things that only concern them.
I'm trying to authorize a users character to delete/update post. I was using policies to do so, but I could only pass one parameter to the policy function. If I pass more than the user and another variable, the variable isn't passed into the function.
Models: User has many characters, a character can post multiple posts. So for authorization purposes, I would have to compare the post's character_id with the current character's id...-
Per the docs, you can pass more multiples to the Gate Facade:
Gate::define('delete-comment', function ($user, $post, $comment) {
//
});
But I couldn't find anyway to do so with policies. What I had to do was to inject the Request object to get the object needed for authorization. Basically I wouldn't even need the User Object.
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
Using the Request object works, but it feels very hacky. Is there a nicer way to achieve this?
edit:
In the CharacterLocationController I have a method show and I want to authorize the action before showing the resource.
public function show(Request $request, Character $character, Location $location)
{
$this->authorize([$location, $character]);
...
}
The policy is registered like this: 'App\Location' => 'App\Policies\LocationPolicy' in the AuthServiceProvider
I dumped the array passed to the policy function, and it only outputs the $location.
public function show(User $user, $data) {
dd($data); // expecting location and character
return !$location->private || $location->authorized->contains($this->character);
}
I think there is possibly some confusion here on what functions are doing what.
When you use
Gate::define('delete-comment', function ($user, $post, $comment) {
//
});
Or in the CommentPolicy
public function delete(User $user, Post $post, Comment $comment)
{
return $user->id === $post->user_id;
}
All you are doing is defining the rules. At this point, we aren't worried about passing anything, only that the objects we received can or should be able to interact with each other. The only difference between these two is when using policies, it's just an easy way to abstract all your rules into one simple and easy to read class. If you have an app with potentially hundreds of tables and models, it will get confusing fast if you have these rules littered all over your app so policies would help to keep them all organized.
It's when you are actually checking if someone has permission to do something when you should be passing these items along. For example, when you do the following,
if (Gate::allows('delete-comment', [$post, $comment])) {
//
}
Or if in the CommentController
$this->authorize('delete', [$post, $comment]);
That is what controls which parameters are going to be passed to the policy or the Gate::define method. According to the docs, the $user parameter is already added for you so in this case, you only need to worry about passing the correct $post and $comment being modified.
I'm trying to add some functionality to the back end of a Bolt CMS installation that does the following:
Check if the user is a member of the "limited editor" group.
If so, only list content which they, personally, own.
This needs to be within the controller, not using Twig.
I've got the user object using
$user = $app['users']->getCurrentUser();
I guess I could use
in_array('limitededitor', $user["roles"]);
But I wondered if there was any existing function in Bolt that would streamline this, like "isAllowed" but for checking role membership?
This is what I've used in the past to determine whether I mount a controller (and thus give access to the new urls), the key part is the users service has a hasRole method but you need to check by user id.
public function checkAuth()
{
$currentUser = $this->app['users']->getCurrentUser();
$currentUserId = $currentUser['id'];
foreach (['admin', 'root', 'developer', 'editor'] as $role) {
if ($this->app['users']->hasRole($currentUserId, $role)) {
return true;
}
}
return false;
}
So I'm working on an admin interface. I have a route set up like so:
Route::controllers([
'admin' => 'AdminController',
]);
Then I have a controller with some methods:
public function getEditUser($user_id = null)
{
// Get user from database and return view
}
public function postEditUser($user_id = 0, EditUserRequest $request)
{
// Process any changes made
}
As you can see, I'm using method injection to validate the user input, so URL's would look like this:
http://example.com/admin/edit-user/8697
A GET request would go to the GET method and a POST request to the POST method. The problem is, if I'm creating a new user, there won't be an ID:
http://examplecom/admin/edit-user/
Then I get an error (paraphrased):
Argument 2 passed to controller must be an instance of EditUserRequest, none given
So right now I'm passing an ID of 0 in to make it work for creating new users, but this app is just getting started, so am I going to have to do this throughout the entire application? Is there a better way to pass in a validation method, and optionally, parameters? Any wisdom will be appreciated.
You can reverse the order of your parameters so the optional one is a the end:
public function postEditUser(EditUserRequest $request, $user_id = null)
{
}
Laravel will then resolve the EditUserRequest first and pass nothing more if there's no user_id so the default value will kick in.