Search code examples
laravelauthorizationpolicylaravel-passport

Laravel - Policy authorization on user and object that both belong to something


I want to authorize my API of my Laravel application. My structure right now is like this:

Users belong to an organization, and the organization has many (lets say) objects. Now I want that only users can view/edit/create/delete objects, that belong to the organization they are part of.

My API route for viewing the objects is:

Route::get('organizations/{id}/objects','ObjectController@indexOrganization')->middleware('auth:api');

I created the Models User, Organization and Object. They all have their own Controller.

I created the ObjectPolicy and tried this:

public function view(User $user, Object $object)
    {
      return $user->organization_id === $object->organization_id;
    }

And then I added ->middleware('can:view,object'); to the route. Unfortunately, it does not work and the Laravel documentation does not provide the information I need.

Can someone help? Thanks!

EDIT

I have no idea what I'm doing wrong!! I Changed everything but I still get a 403 response.

Here is my code:

Route:

Route::get('organizations/{organization}/objects','ObjectController@index Organization')->middleware('auth:api', 'can:view, organization');

OrganizationPolicy:

public function view(User $user, Organization $organization)
{
    return $user->organization_id === $organization->id;
}

ObjectController:

public function indexOrganization(Organization $organization)
{
  $objects = $organization->objects;
  return ObjectResource::collection($objects);
}

I also added this to my AuthServiceProvider:

protected $policies = [
    'App\Model' => 'App\Policies\ModelPolicy',
    App\Organization::class => App\Policies\OrganizationPolicy::class,
];

EDIT 2 / SOLUTION

The answer from newUserName02 works! The problem was inside the AuthServiceProvider. After I changed the code (see above in Edit) there to:

protected $policies = [
    'App\Model' => 'App\Policies\ModelPolicy',
    'App\Organization' => 'App\Policies\OrganizationPolicy',
];

it worked!


Solution

  • The policy method should to match the arguments you are passing to the controller. It looks like you are passing the id of the Organization in the route, but you are trying to check the Object on the policy.

    https://laravel.com/docs/5.7/authorization#via-middleware

    You can take advantage of Laravel's implicit model binding to inject the Organization into the controller like this:

    Route:

    Route::get('organizations/{organization}/objects','ObjectController@indexOrganization')->middleware('auth:api', 'can:view,organization');
    

    Policy:

    public function view(User $user, Organization $organization)
    {
      return $user->organization_id === $organization->id;
    }
    

    Controller:

    public function indexOrganization(Organization $organization)
    {
        ...
    }
    

    Notice that {organization} in the route matches organization in the ->middleware() call, which matches $organization in the policy and controller.