Laravel 5.5 permissions based on user id

For a Laravel 5.5 project I solved a permission problem that I’d like to share in case someone else can benefit.

Problem:

The structure of our urls look like this:

users/5/companies

Which will show all the companies for user 5. I however wanted to limit it so that only user 5 may see user/5 company listings. User/4 or another use should see access denied when viewing /users/5/companies.

My current route was defined this way in routes/web.php:

Route::prefix('users/{user}')->group(function () {

  Route::get('companies', 'CompaniesController@my_companies')->name('companies.userindex');

});

Solution:

Use middleware attached to a route group with prefix to do the check. Although you can accomplish this in a controller, you are better off using middleware. Middleware intercepts the request before it even hits your controllers. So you can stop it at an earlier stage.

Step 1. Create Middleware. I’ll call it SectionOwner, you can name it as you like. Run this command from the command line. This command will create your middleware and give you some boilerplate code.

php artisan make:middleware SectionOwner

Step 2. Visit Your SectionOwner Middlewarefile located in : /app/Http/Middleware. Change the handle function from:

    public function handle($request, Closure $next)
    {
        return $next($request);
    }

To

    public function handle($request, Closure $next)
    {

      if( $request->user() !== $request->route('user')->id ){

        abort(403, 'Access denied');
        
      }

      return $next($request);

    }

In my case, I can simply compare id of the requester with route param {user}

Step 3.  Tell Laravel Kernal.php about the new Middleware so that we can use it on our routes.

Look in your kernal.php and find the array for $routeMiddleware. You will have to add your MiddleWare with the full namespace.  I added it and aliased it as section.owner

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'section.owner' => \App\Http\Middleware\SectionOwner::class,
    ];

Step 4. Add to routes

Now we can use our middleware in our web.php route file. I will also add the auth middleware so that if the user is not logged in it will ask them to login.

My web.php now becomes:

Route::prefix('users/{user}')->group(function () {

  Route::get('companies', 'CompaniesController@my_companies')->name('companies.userindex')->middleware('auth', 'section.owner');

});

Now you have it,  the path users/5/companies is now secure and only user/5/ may access it.

Optional: You may also add the middleware to the entire prefix group so that users/5/* is only accessible by user 5.

Route::middleware(['auth', 'section.owner'])->prefix('users/{user}')->group(function () {

  Route::get('companies', 'CompaniesController@my_companies')->name('companies.userindex');

});

Bonus

We also have roles that are used for permissions. We wanted admins to be able to access any user’s company listing. I Added a permissions check to allow admins access.

    public function handle($request, Closure $next)
    {
      $user = $request->user();
      if( $user->id !== $request->route('user')->id ){

        if( $user->cant('access-owner-sections')){
          abort(403, 'Access denied');
        }
      }

      return $next($request);

    }

Warning: You still have to secure your company, edit, delete, store, etc… routes. I’ve done that separately outside of this post.

Thanks for reading! I hope you found this helpful.

Leave a Reply

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