Skip to content

SharedPerimeter excludes all results even with valid shared records #27

@f-rosito

Description

@f-rosito

Environment

  • Laravel: 11.9
  • PHP: 8.2
  • lomkit/laravel-access-control: 0.1.5
  • DB: Mysql 8

Describe the bug
When adding a SharedPerimeter to the Control, users with valid shared relationships and correct permissions (view shared products) receive no results, whereas they should be able to access shared models.

If the SharedPerimeter is removed from the control, the same user correctly accesses their own records via OwnPerimeter.


Expected behavior
Users with the view shared products permission and valid shared relationships (via sharedUsers() many-to-many) should receive matching shared records when SharedPerimeter is active.


Reproduction

Permission::firstOrCreate(['name' => 'view global products']);
Permission::firstOrCreate(['name' => 'view own products']);
Permission::firstOrCreate(['name' => 'view shared products']);
$userA = User::create([...]);
$userA->givePermissionTo(['view own products']);
Product::factory()->count(2)->create(['user_id' => $userA->id]);

$userB = User::create([...]);
$userB->givePermissionTo(['view own products', 'view shared products']);
Product::factory()->count(2)->create(['user_id' => $userB->id]);

$sharedProduct = Product::where('user_id', $userA->id)->first();
$sharedProduct->sharedUsers()->attach($userB->id);

Then with this Control:

class ProductControl extends Control
{
    /**
     * Define the perimeters used for access control on products.
     *
     * @return array<\Lomkit\Access\Perimeters\Perimeter>
     */
    protected function perimeters(): array
    {
        return [
            GlobalPerimeter::new()
                ->allowed(fn($user, $method) => $user->can("$method global products"))
                ->should(fn($user, $model) => true)
                ->query(fn($query, $user) => $query),

            SharedPerimeter::new()
                ->allowed(fn($user, $method) => $user->can("$method shared products"))
                ->should(fn($user, $model) => $model->sharedUsers->contains($user))
                ->query(fn($query, $user) =>
                    $query->whereHas('sharedUsers', fn(Builder $q) => 
                        $q->where('product_user_shared.user_id', $user->id)
                    )
                ),

            OwnPerimeter::new()
                ->allowed(fn($user, $method) => $user->can("$method own products"))
                ->should(fn($user, $model) => $model->user_id === $user->id)
                ->query(fn($query, $user) => $query->where('user_id', $user->id)),
        ];
    }
}

With this configuration:

  • OwnPerimeter works as expected
  • SharedPerimeter causes the final request to return []

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions