skip to Main Content

So far the only thing I’ve seen devs putting in model policies is simple statements like this:

public function update(User $user, Post $post): bool
{
    return $user->id === $post->user_id;
}

In my app I have an admin user that should be able to edit all posts, currently that’s handled on the controller side of my app, but is it a good idea to move it to the model policy (with a condition if($user->is_admin) return true) or should that logic live somewhere else?

asking because Laravel seems to have a whole bunch of conventions and I want to make sure I am using the proper ones.

3

Answers


  1. Yes, you should, but maybe put the admin logic in the AuthServiceProvider

    Model Policies are one of the correct places to put authorization logic. And they could be more than just one line.

    But mostly it is better to write logic for admins, or superAdmins only once:

    public function boot(): void
    {
        Gate::before(function ($user, $ability) {
            return $user->hasRole('Super Admin') ? true : null;
        });
    }
    

    This example is copied from spaties laravel-permission package: https://spatie.be/docs/laravel-permission/v6/basic-usage/super-admin

    Login or Signup to reply.
  2. There are multiple ways of doing one thing in Laravel.
    You can always set the conditions to model policy

    For certain users(Admin & Super Admin) to have complete authorization for all actions in a policy. You can do this by creating a "before" method in the policy. This method runs before any other and allows you to authorize the action beforehand. It’s commonly used to let application administrators perform any action without restrictions.

    public function before(User $user, string $ability): bool|null
    {
    if ($user->isAdministrator()) {
        return true;
    }
    
    return null;
    }
    

    https://laravel.com/docs/10.x/authorization#policy-filters

    And if you have full authorization for admin | superadmin through out your application then you can put the logic in AuthServiceProvider using Gates

    public function boot(): void
    {
    Gate::before(function ($user, $ability) {
        return $user->hasRole('Administrator') ? true : null;
    });
    }
    
    Login or Signup to reply.
  3. In my opinion, all authorization logic should be in a policy. Instead of having to put $user->isAdmin in every method of the policy, you can make a parent abstract class policy where the before function is defined. Sometimes though, even the admin shouldn’t be allowed to do certain actions, so we need to declare exceptions as well, this is implemented as follows:

    abstract class Policy
    {
       protected array $adminExceptions = [];
    
        public function before($user, $ability)
        {
            if (in_array($ability, $this->adminExceptions)) {
                return;
            }
    
            if ($user?->isAdmin) {
                return true;
            }
        }
    }
    
    class MyPolicy extends Policy
    {
        protected array $adminExceptions = ['delete'];
    
    
        // Admin won't have to go through this policy
        public function update(User $user, Post $post): bool
        {
            return $user->id === $post->user_id;
        }
    
    
        // Admin will still need to go through this policy
        public function delete(User $user, Post $post): bool
        {
            return $user->id ==== $post->user_id
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search