Laravel 5.2 authorize using user model's can method - laravel-5

I have a problem when using user model's can and cannot method. I have a post model and an user model. And I define an add method in PostPolicy class:
public function add(User $user){
if($user->isGuest()) return false;
return true;
}
I know when use can or cannot method, the policy class which will be used will be resolved automatically by model instance we pass to can or cannot method, like $user->can('update',$post). But in this example. the add method does not need a post instance, So I can't do like this $user->can('add',$post). Can someone tell me how to get done it?

It's a good question.
The funny thing is this isn't documented for Laravel 5.2 but it is for 5.4. So you will have to check if this works for you in Laravel 5.2.
When you don't have an object model to pass into the policy check the documentation says you should pass this instead Model::class. In your case, it'd be Post::class as the parameter. So it'd be:
$user->can('update', Post::class);
If this does not work for you in Laravel 5.2 you can always write a Gate instead of a policy and just use the Gate::allows() or Gate::denies methods:
https://laravel.com/docs/5.2/authorization#gates

Related

Livewire, Laravel - can I call a controller 'store' method from a Livewire component?

I have a Laravel controller with a 'store' method. i.e. store(Request $request).
For the view I want to embed a livewire component and then utilize the existing controller 'store' method (behind the scenes) from a livewire component method. Is this possible? Currently, I'm running into the problem that there is no Request object since I'm no longer making the call from the existing route/view (i.e. POST /orders).
public function oms_order()
// this method provides the POST /orders leveraging
// Controllers\OrderController#store
{
$this->refId = app('App\Http\Controllers\OrderController')->store($this->jsonOrder);
}
Argument 1 passed to App\Http\Controllers\OrderController::store() must be an instance of Illuminate\Http\Request, null given, called in D:\xampp\htdocs\jade\livewire_hpp\app\Http\Livewire\Oms.php on line 26
I can remove the 'Request $request' from the store method but then that breaks the standard Laravel POST /orders route.
I was hoping to use the existing laravel app as the backend/API and add the livewire bit for a demo. Any advice is welcome.
I don't think it is a best practice to call a controller function from another class. If you want reusability, I suggest refactoring code by creating a separate class with a store method which accepts only required attributes and saves a record.
Then you can use this method in multiple places. It is easier to test also.
It is difficult to give an exact answer before seeing your store method.

Are there any methods in Laravel that automatically add a model to an authenticated user?

I find myself doing this frequently:
$model→user()→save(Auth::user());
This requires importing the Auth facade into every controllers where the relationship is being saved.
I was thinking of possibly create a simple method (saveWithAuthUser()) under User model to save the authenticated user.
Just wondering if this already exists as a Laravel method already or not, and if not, is this a pointless idea?
This requires importing the Auth facade into every controllers where
the relationship is being saved.
You can avoid this by getting the user from the Request instead.
public function create(Request $request) {
$model->user()->associate($request->user());
}
There is also a global helper for exactly this.
$user = auth()->user();

Should all controllers have only the basic CRUD methods?

In Laravel, are all controllers only supposed to have the basic CRUD methods, as shown in the link below?
https://laravel.com/docs/5.3/controllers#resource-controllers
That is, should the only methods in a controller be:
index()
create()
store()
show()
edit()
update()
destroy()
Thanks.
No.
A controller can have methods named however you want!
If you are creating a RESTful controller, then the names of the methods make sense.
When you create a Resource Controller, then Laravel will save you the pain of writing the routes (you can use Route::resource)
For example: you can do this in YourController.php
function tada() {
return "Tadaaaa";
}
and then in your routes.php, you define a route like
Route::get('tada', 'YourController#tada');
And visiting that route will present you with the string Tadaaaa
Have fun!
No, You can have your own functions too. This is just a boilerplate that Laravel provides you to start with.

Laravel 5.1 index & create authorization

I'm using Laravel 5.1's authorization features, documented here. My controllers implement AuthorizesRequests and I have my policies set up connecting policies to their models to create an ACL of sorts. In my controllers, I'm checking for authorization in each method. For example, in an 'AgencyController' the 'update' method calls $this->authorize($agency), which then checks my AgencyPolicy's update method to know rather or not the current user is allowed to update the agency, just as described in the documentation. This works the way I want it to.
However, what I can't seem to figure out is how to use authorization for other methods like index() and create() where there isn't a specific model being used. Calling $this->authorize('index') seems to return false, even if I have an index($user) function in my policy class that only returns true.
I'm new to using Laravel's authorization helpers, so I might be going about this wrong or missing something obvious. Any help pointing me in the right direction would be gretaly appreciated!
You have to pass it the class name of the model you're checking:
$this->authorize('index', Agency::class);
With some help from someone in the Laravel slack group I was able to find the answer to this myself.
Without an instance of the model, the authorize() calls couldn't map to the correct policies. But by simply passing the class to them, it is able to and works.
For example, instead of calling $this->authorize('index') in my controller's index method, I'm now calling $this->authorize('index', Agency::class) to give it the correct model to use.

Authorization Policy without model

I need to authorize users on a forum.
So in blade, I have #can('editPost', $post) before showing the form to reply to a topic. My PostPolicy class has a editPost method that validates to true if it's the users own post.
However, the issue appears when I want to do a simple check, like deletePost(). This checks to see if Auth::user()->isAdmin
public function deletePost(User $user) {
return Auth::user()->isAdmin;
// return $user->isAdmin
}
However, this won't even get called, since I'm not passing an instance of Post
My real world application is much more complicated, but I'm using isAdmin as a simple example.
I guess defining $gate->define('deletePost', 'App\Policies\PostPolicy#deletePost'); in AuthServiceProvider could work, but would end up separating my definitions and methods, and ultimately for a large app clutter the AuthServiceProvider
When you register a policy it is the classname that is used to route checks to the class, so in order to get routed to the policy you can just pass the class name of the type you registered it with.
Try using #can('delete', Post::class) and see if that gets you there
refer to
Illuminate\Auth\Access\Gate::firstArgumentCorrespondsToPolicy
EDIT
After a little more diggin I found this
https://github.com/laravel/framework/commit/70f75255808ffc96275e6f2f356616dd2e163434#diff-961368895033e553787b301c3be0e17a
so it looks like if you on version 5.1.23 then you will be able to pass a string otherwise your will need to just pass new Post
In controllers
$this->authorize('<ability>', <Class-With-Rule::class> | <Full-Path-To-Class>);
In Blade view
#can('<ability>', <Class-With-Rule>::class> | <Full-Path-To-Class>)
In Eloquent model
$user->can('<ability>', <Class-With-Rule>::class> | <Full-Path-To-Class>);
Methods Without Models:
Some policy methods only receive the currently authenticated user and not an instance of the model they authorize. This situation is most common when authorizing create actions. For example, if you are creating a blog, you may wish to check if a user is authorized to create any posts at all.
When defining policy methods that will not receive a model instance, such as a create method, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user:
https://laravel.com/docs/7.x/authorization

Resources