-
Notifications
You must be signed in to change notification settings - Fork 1
Description
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.