How to validate object ownership in Laravel

by Jeffrey van Rossum

There are several ways in which you can determine if a user owns an object in your Laravel application. This post will give you a couple of examples how you could implement this check within your form request validations.

An example use case

Let's take this example use case: a user creates an order and assigns it to a customer through a web form. However, one could abuse the form and alter the customer_id to a customer that doesn't belong to the current user.

Using findOrFail in your controller function

The simplest way to prevent this from happening is to lookup the customer on the user customers relation. This is assuming you have a hasMany relation on your User model for customers.

auth()->user()->customers()->findOrFail(request('customer_id'));

Now when the customer is not found, it will result in a 404 error. This is a solid way, but it doesn't provide feedback to the user and isn't handled within the validation rules. Let's look at how we could do that.

Using a Closure

If you need this sort of validation only once, you could implement this in your validation using a Closure. That would look like this:

request()->validate([
    'customer_id' => [
        'required', 
        function ($attribute, $value, $fail) {
            if (auth()->user()->customers()->where('customers.id', $value)->doesntExist()) {
                $fail($attribute.' does not belong to you.');
            }
        },
    ]
]);

Using a custom validation rule

Now if you need this sort of check multiple times throughout your application, you could best make use of a Rule object. You can create one by running the following artisan command.

php artisan make:rule Ownership

This class will be created within your app/Rules directory. In this example, the Ownership rule will accept a model (string or instance) as parameter. Then we will try and find that instance of the model and check if the model's user_id is the same as the current user's id.

class Ownership implements Rule
{
    protected $model;

    public function __construct($model)
    {
        $this->model = $model;
    }

    public function passes($attribute, $value)
    {
        $object = is_object($value) ? $value : (new $this->model)->findOrFail($value);

        return $object->user_id === auth()->id();
    }

    public function message()
    {
        return 'This object does not belong to you.';
    }
}

Sometimes you might not use the user_id column but maybe something like owner_id. You could make that changeable by adding an additional parameter for this column name to the constructor.

Now to implement this in your validation rules, you can do the following:

request()->validate([
    'customer_id' => ['required', new Ownership('App\Customer')]
]);

In closing

I think all options are fine and there might even be betters solutions. If you use one of the solutions in this post, the one you should use depends on whether you would want to display a validation message and if you find yourself doing this type of check multiple times.

This post was last modified 12 May 2020

Comments

Talk about this article on X.
Did you like this post?

If you sign up for my newsletter, I can keep you up to date on more posts like this when they are published.