Docs: Correct :linked scope documentation

This commit is contained in:
Moritz 2026-01-08 22:54:53 +01:00
parent 4fffeeaaa0
commit b3eb6c9223
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 16 additions and 12 deletions

View file

@ -110,8 +110,8 @@ Control access to LiveView pages:
Three scope levels for permissions:
- **:own** - Only records where `record.id == user.id` (for User resource)
- **:linked** - Only records linked to user via relationships
- Member: `member.user_id == user.id`
- CustomFieldValue: `custom_field_value.member.user_id == user.id`
- Member: `id == user.member_id` (User.member_id → Member.id, inverse relationship)
- CustomFieldValue: `member_id == user.member_id` (traverses Member → User relationship)
- **:all** - All records, no filtering
**6. Special Cases**
@ -714,8 +714,8 @@ defmodule Mv.Authorization.Checks.HasPermission do
- **:all** - Authorizes without filtering (returns all records)
- **:own** - Filters to records where record.id == actor.id
- **:linked** - Filters based on resource type:
- Member: member.user_id == actor.id
- CustomFieldValue: custom_field_value.member.user_id == actor.id (traverses relationship!)
- Member: `id == actor.member_id` (User.member_id → Member.id, inverse relationship)
- CustomFieldValue: `member_id == actor.member_id` (CustomFieldValue.member_id → Member.id → User.member_id)
## Error Handling
@ -799,12 +799,14 @@ defmodule Mv.Authorization.Checks.HasPermission do
defp apply_scope(:linked, actor, resource_name) do
case resource_name do
"Member" ->
# Member.user_id == actor.id (direct relationship)
{:filter, expr(user_id == ^actor.id)}
# User.member_id → Member.id (inverse relationship)
# Filter: member.id == actor.member_id
{:filter, expr(id == ^actor.member_id)}
"CustomFieldValue" ->
# CustomFieldValue.member.user_id == actor.id (traverse through member!)
{:filter, expr(member.user_id == ^actor.id)}
# CustomFieldValue.member_id → Member.id → User.member_id
# Filter: custom_field_value.member_id == actor.member_id
{:filter, expr(member_id == ^actor.member_id)}
_ ->
# Fallback for other resources: try direct user_id
@ -918,7 +920,7 @@ end
**Location:** `lib/mv/membership/member.ex`
**Special Case:** Users can always access their linked member (where `member.user_id == user.id`).
**Special Case:** Users can always READ their linked member (where `id == user.member_id`).
```elixir
defmodule Mv.Membership.Member do
@ -978,10 +980,10 @@ defmodule Mv.Membership.CustomFieldValue do
policies do
# SPECIAL CASE: Users can access custom field values of their linked member
# Note: This traverses the member relationship!
# Note: This uses member_id relationship (CustomFieldValue.member_id → Member.id → User.member_id)
policy action_type([:read, :update]) do
description "Users can access custom field values of their linked member"
authorize_if expr(member.user_id == ^actor(:id))
authorize_if expr(member_id == ^actor(:member_id))
end
# GENERAL: Check permissions from role

View file

@ -294,7 +294,9 @@ Each Permission Set contains:
**:own** - Only records where id == actor.id
- Example: User can read their own User record
**:linked** - Only records where user_id == actor.id
**:linked** - Only records linked to actor via relationships
- Member: `id == actor.member_id` (User.member_id → Member.id, inverse relationship)
- CustomFieldValue: `member_id == actor.member_id` (traverses Member → User relationship)
- Example: User can read Member linked to their account
**:all** - All records without restriction