Skip to content

Empty has_many associations incorrectly return true in permission checks #52

@vincentvanbush

Description

@vincentvanbush

Description

The check_association/3 function in lib/permit/permissions/parsed_condition.ex uses Enum.all?/2 to check if all records in a has_many association satisfy permission conditions. However, this causes incorrect behavior for empty associations because Enum.all?([], fn -> ... end) returns true (vacuous truth).

Problem

When checking permissions on a record with an empty has_many association:

condition = {:actors, {:==, [age: 123]}}
test_object = %Movie{actors: []}

# Currently returns true (incorrect!)
ConditionParser.build(condition)
|> ParsedCondition.satisfied?(test_object, nil)

Expected: Should return false because there are no actors matching age 123.
Actual: Returns true due to Enum.all?([]) returning true.

Real-world Impact

This affects permission rules like:

permit()
|> read(Event, invitations: [user_id: user.id])

An event with zero invitations would incorrectly grant access to any user.

Solution

Change line 117 in lib/permit/permissions/parsed_condition.ex from:

Enum.all?(sub_assoc, &check_conditions(&1, assoc_conditions))

To:

Enum.any?(sub_assoc, &check_conditions(&1, assoc_conditions))

This implements "AT LEAST ONE" (existential) semantics, where:

  • Empty associations correctly return false
  • At least one matching record returns true

Tests

A failing test has been added demonstrating this issue. With the fix, all tests pass.

Related Issues

This is related to curiosum-dev/permit_ecto#22, which discovered the inconsistency between Ecto query behavior and the permission check behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions