Handy access management validation rule for Laravel 5

I worked on several Laravel projects and in almost every project there was a need for some sort of access management control system. Most basic example is validation whether some resource belongs to user. For instance, imagine you have site for freelancers where users can manage their projects, timelogs, invoices etc. When user wants to submit new timelog for some project he needs to select that project from dropdown menu and populate rest of the form. When he submits that form to an API we need to properly validate that request. Let’s say that dropdown menu has its name property set to “project_id”.

$rules = [
    // other rules
    'project_id' => 'required|exists:projects,id',
    // other rules
];

The problem

Next important step is to validate whether that project belongs to authenticated user. Now, this is where you have a lot of choices. You can use Illuminate\Auth\Access\Gate class and define user Abilities (see more) or you can write your custom checks in controllers. In my case, I wanted something that requires far less code and can be used as a validation rule. That would solve 2 problems at once:

  1. Reusable code that can be applied to almost every resource
  2. Effortless displaying errors regarding non authorized request

Solution

Basically, to validate whether some resource belongs to user I can do something like this:

$rules = [
    // other rules
    'project_id' => 'required|exists:projects,id|belongs_to_user:projects',
    // other rules
];

What this belongs_to_user  validation rule does is checking whether given project id matches id in projects table. It also checks if user_id is the id of authenticated user. To add this custom validation rule we need to add custom service provider class with following command:

php artisan make:provider CustomValidationServiceProvider

Next step is to register new service provider class. That can be done by opening config/app.php  file and adding App\Providers\CustomValidationServiceProvider::class to providers array. Now, new file will be created in app/Providers  directory. Open newly generated file and add this code into body of boot method:

public function boot()
{
    Validator::extend('belongs_to_user', function($attribute, $value, $parameters, $validator) {
        $table = $parameters[0];
        $primary_key_field = isset($parameters[1]) ? $parameters[1] : 'id';
        $user_foreign_key_field = isset($parameters[2]) ? $parameters[2] : 'user_id';

        return DB::table($table)->where($primary_key_field, '=', $value)->first()->$user_foreign_key_field == Auth::user()->id;
    });
}

If you look at the code, there are 2 optional parameters. One parameter is for specifying custom primary key field for our resource (if your primary key field is not “id”). Other parameter is for specifying custom user foreign key field (if you’re not using user_id  as foreign key for users  table).

And that’s it. We added new validation rule with few lines of code. I hope this article will be helpful. Thanks for reading! Looking forward to your comments!

7 Replies to “Handy access management validation rule for Laravel 5

  1. This custom validation rule implementation shows really great example of code reusability.
    Also, it seems like this custom validation role is a must when developing bigger applications to avoid repeating yourself with checking user’s permissions (like “belongs to”) in the authorize() method of Laravel’s request class – instead, your solution solves this problem in the rules() method of request class. Really elegant solution. Cheers 🙂

    P.S.
    ‘project_id’ => ‘required|exists:projects,id|belongs_to_user:projects,custom_id,custom_user_id’

    – is that right for changing the default project id column and user id column?

    1. This is more suitable when user can define relationship between his models. This rule is for preventing user from choosing someone else’s model instance.

Leave a Reply to Luka Bartolic Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.