Using a single policy method to cover every action on a resource - laravel

I have a resource named Post. Every Post is related to a single User, and a User may have several (or zero) Posts.
I'm using Route::resource to map routes for every possible action on the Post resource to a single resource controller, PostController.
Now, I wish to implement a policy for the Post resource. Aiming to keep everything as simple and fool-proof as possible, I'd like to make the policy as follows:
Every user is authorized to make any action that doesn't require an existing Post (e.g. create).
A User is only authorized to access its own Posts for every action that accesses a Post (e.g. edit, update, delete).
What I'm trying to do right now is to create a single policy method called access, which would check that the Post is owned by the User. Then, in my PostController, every method that has a Post object in its parameters (such as edit(Post $post)) would begin with
$this->authorize('access', $post);
However, I don't like that I need to put those lines manually. If I ever forget one of those, I'll have a security hole right there.
On the other hand, I have the authorizeResource method, which makes authorization automatic but requires the policy to have several methods so they are mapped to the each of the controller's methods. Also, I tried to use the Authorize/can middleware, but it didn't work (maybe because I used it on a Route::resource mapping).
The question is: What would be the cleanest and more secure way to achieve the result I described (i.e. authorizing every possible action that accesses a resource with the exact same rule)?

You can use authorizeResource() and override the resourceAbilityMap() method in your controller. The latter method returns a mapping from controller methods to the policy methods that will be called.
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Auth/Access/AuthorizesRequests.php#L105
E.g.
class MyController extends Controller
{
// ...
/**
* Get the map of resource methods to ability names.
*
* #return array
*/
protected function resourceAbilityMap()
{
return [
'edit' => 'access',
'update' => 'access',
'destroy' => 'access',
];
}
// ...
}

Related

how to implement Single Responsibility in laravel

I am so confused about how to implement and how to follow SRP (single responsibility principle ) in a Laravel controller.
Suppose we have a controller which we have to do these things:
e.g
public function StorePost() {
// check user login()
//check number of current user Post count =>which must be less than 10
//store post
//send an email to user which your post has saved
//return =>api:json /web : redirect
}
I know that I can implement some DB queries in the repository but I don't know how to implement others of my logic code to achieve SRP
Also, I know there is a Heyman package to achieve these but I want to implement it by myself.
SRP in this context basically means each class and method should only be responsible for a single behaviour/feature. A rule of thumb is a class or method should change for one reason only, if it changes for multiple reasons, it needs to be broken down into smaller parts.
Your storePost method should not bother with checking the user login, that should be handled elsewhere before invoking storePost. storePost shouldnt change if the auth mechanism changes like switching from api token to json web token or something else. Laravel does this in the middleware level with the auth middleware.
Checking the users post count, this can be checked in the validation stage. storePost shouldn't change if we add more validation logic. In Laravel you can use FormValidation for this
For storing the post, the controller doesn't need to know how to call the DB, you can use the active record style using the model class or maybe create a service or repository class if your use case requires that. storePost shouldn't change if we decide to change DB vendor like going NoSQL.
For sending email, again the controller doesnt need to know how to send the email like what the subject/body recipients are. storePost shouldnt change if we need to change the email layout. Laravel has Notification for that
For serialising the response to json, the controller doesnt need to know how to format the response. if we decide to update how our json looks, storePost shouldnt change. Laravel has API Resources for that
So, ultimately in this example, the responsibility of the controller method is basically to glue all these together. It basically does what you wrote down, it only responsible for maintaining the step by step behavior, everything else is delegated to someone else. if the behavior change, like adding new behavior e.g notify all follower, storePost will change.

MiddleWare vs CustomRequest for check user rights

Let's say there some users who have several posts. Every user has many posts, and every post belongs to one user.
To change post frontend uses URL like users/1/posts/3. The goal is to check, that post number 3 belongs to user number 1.
There are several ways to do it:
Check inside controller method or service(bad method IMHO)
Check inside custom request (authorize function)
Check inside middleware
I choose between the last 2, but have some doubts. The custom request should contain validation rules and do not be linked with authorization(SOLID), and I don't know if it's good to do it inside middleware.
I assume you want to check if user can modify the post.
For authorizing access https://laravel.com/docs/7.x/authorization#creating-policies is a way to go.
After defining policy you can reuse it in blade files, in controller and etc..
The policy defines rules for standard actions like create, read, update, delete.
For blades then you can:
#can('update', $post)
For methods in controllers:
$this->authorize('update', $post)
For anywhere else:
$user->can('update', $post)
You can use Policies in conjunction with custom Form Requests

laravel middleware setting $request attribute, does it affect performance

In one of my middleware I have used something like this
$user = [
'name' => 'noob',
'phone' => '87548154'
]; /* which actually comes from redis cache */
$request->attributes->set('user', $user);
and in the controller i use it like
$request->get('user')['name']
OR
$request->get('user')['phone']
As this seems very flexible, I would like to attach more data into the $user array.
In the laravel docs its written above the get() method of Request class is
* Gets a "parameter" value from any bag.
* This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
* flexibility in controllers, it is better to explicitly get request parameters from the appropriate
* public property instead (attributes, query, request).
* Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
My question is, is it going to be a good idea? because the most frequently used data is already attached in the middleware. So that I dont have to write extra codes in the controller methods again and again. Will it affect on performance for a high traffic server?
I personnally never work this way. You can access the current user from anywhere using the Auth facade as following :
\Auth::user()
It enable you to never send it when unnecessary and still use it from anywhere (controllers, models, blades or everything else).
Then to access your properties :
\Auth::user()->phone
and so on...

Laravel 5 user role check before

I have some trouble figuring out how to do this properly.
I have Users that can create Articles which can be viewed, edited and deleted. I want to restrict the edit and delete actions if the currently logged in user is not the creator of the Article.
I have already done this using Policies and use Gate within a Form Request, but if i understand correctly FormRequest is only for POST requests. And I currently pass the FormRequest as a parameter to the getEdit() action. That seems wrong.
So how can I make sure the user can neither access nor edit the Article?
If you already defined a Policy you can directly access it using the Gate facade within your controller action like this:
public function getEdit($id)
{
$reference = Reference::findOrFail($id);
if (Gate::denies('owns-reference', $reference))
abort(403);
return view('reference.edit')
->with('reference', $reference);
}
Just make sure to include the Gate on top of your file like this:
use Gate;

Adding attribute to a user when register in Laravel 5.1

When a user Register, I want to add location data for the user.
I get those with GeoIP.
So, each time a user is created, I would like somewhere to add Country, City, etc.
I was thinking about setting Hidden fields in view, but I think it a hugly way to do it, and I'm sure there is a better way to do...
Any Idea???
Any time I create a record that needs extra data, involves inserting additional records into additional tables, etc, I create a service class. Something like "UserCreator" then I pass it the input, do any additional operations, wrap multiple database calls in a transaction and so on.
That said there are so many ways to do what you want. You could Input::merge(...) then save, you could separate the process of creating a user from your controller / route function, etc.
If you are just getting started with Laravel and/or your project is rather simple, then you probably want to look at Input::merge
I solved it using Request Contructor as said here in laracast
In the Form Request's constructor I inject \Illuminate\Http\Request. I can then add my value to the request instance as such:
public function __construct(\Illuminate\Http\Request $request)
{
$request->request->add(['date_of_birth' => implode('-', $request->only('year', 'month', 'day'))]);
}

Resources