Document CustomFieldValue policies and own_data create/destroy in architecture
Update roles-and-permissions-architecture.md with policy layout and permission matrix for CustomFieldValue (linked).
This commit is contained in:
parent
4e032ea778
commit
db95979bf5
1 changed files with 23 additions and 21 deletions
|
|
@ -501,9 +501,11 @@ defmodule Mv.Authorization.PermissionSets do
|
|||
%{resource: "Member", action: :read, scope: :linked, granted: true},
|
||||
%{resource: "Member", action: :update, scope: :linked, granted: true},
|
||||
|
||||
# CustomFieldValue: Can read/update custom field values of linked member
|
||||
# CustomFieldValue: Can read/update/create/destroy custom field values of linked member
|
||||
%{resource: "CustomFieldValue", action: :read, scope: :linked, granted: true},
|
||||
%{resource: "CustomFieldValue", action: :update, scope: :linked, granted: true},
|
||||
%{resource: "CustomFieldValue", action: :create, scope: :linked, granted: true},
|
||||
%{resource: "CustomFieldValue", action: :destroy, scope: :linked, granted: true},
|
||||
|
||||
# CustomField: Can read all (needed for forms)
|
||||
%{resource: "CustomField", action: :read, scope: :all, granted: true}
|
||||
|
|
@ -678,7 +680,7 @@ Quick reference table showing what each permission set allows:
|
|||
| **User** (all) | - | - | - | R, C, U, D |
|
||||
| **Member** (linked) | R, U | - | - | - |
|
||||
| **Member** (all) | - | R | R, C, U | R, C, U, D |
|
||||
| **CustomFieldValue** (linked) | R, U | - | - | - |
|
||||
| **CustomFieldValue** (linked) | R, U, C, D | - | - | - |
|
||||
| **CustomFieldValue** (all) | - | R | R, C, U, D | R, C, U, D |
|
||||
| **CustomField** (all) | R | R | R | R, C, U, D |
|
||||
| **Role** (all) | - | - | - | R, C, U, D |
|
||||
|
|
@ -1053,35 +1055,33 @@ end
|
|||
|
||||
### CustomFieldValue Resource Policies
|
||||
|
||||
**Location:** `lib/mv/membership/custom_field_value.ex`
|
||||
**Location:** `lib/membership/custom_field_value.ex`
|
||||
|
||||
**Special Case:** Users can access custom field values of their linked member.
|
||||
**Pattern:** Bypass for READ (list queries), CustomFieldValueCreateScope for create (no filter), HasPermission for read/update/destroy. Create uses a dedicated check because Ash cannot apply filters to create actions.
|
||||
|
||||
```elixir
|
||||
defmodule Mv.Membership.CustomFieldValue do
|
||||
use Ash.Resource, ...
|
||||
|
||||
policies do
|
||||
# SPECIAL CASE: Users can access custom field values of their linked member
|
||||
# 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"
|
||||
# Bypass for READ (list queries; expr triggers auto_filter)
|
||||
bypass action_type(:read) do
|
||||
description "Users can read custom field values of their linked member"
|
||||
authorize_if expr(member_id == ^actor(:member_id))
|
||||
end
|
||||
|
||||
# GENERAL: Check permissions from role
|
||||
policy action_type([:read, :create, :update, :destroy]) do
|
||||
description "Check permissions from user's role"
|
||||
# CREATE: CustomFieldValueCreateScope (no filter; Ash rejects filters on create)
|
||||
# own_data -> create when member_id == actor.member_id; normal_user/admin -> create (scope :all)
|
||||
policy action_type(:create) do
|
||||
authorize_if Mv.Authorization.Checks.CustomFieldValueCreateScope
|
||||
end
|
||||
|
||||
# READ/UPDATE/DESTROY: HasPermission (scope :linked / :all)
|
||||
policy action_type([:read, :update, :destroy]) do
|
||||
authorize_if Mv.Authorization.Checks.HasPermission
|
||||
end
|
||||
|
||||
# DEFAULT: Forbid
|
||||
policy action_type([:read, :create, :update, :destroy]) do
|
||||
forbid_if always()
|
||||
end
|
||||
# DEFAULT: Ash implicitly forbids if no policy authorized (fail-closed)
|
||||
end
|
||||
|
||||
# ...
|
||||
end
|
||||
```
|
||||
|
||||
|
|
@ -1089,11 +1089,13 @@ end
|
|||
|
||||
| Action | Mitglied | Vorstand | Kassenwart | Buchhaltung | Admin |
|
||||
|--------|----------|----------|------------|-------------|-------|
|
||||
| Read linked | ✅ (special) | ✅ (if linked) | ✅ | ✅ (if linked) | ✅ |
|
||||
| Update linked | ✅ (special) | ❌ | ✅ | ❌ | ✅ |
|
||||
| Read linked | ✅ (bypass) | ✅ (if linked) | ✅ | ✅ (if linked) | ✅ |
|
||||
| Update linked | ✅ (scope :linked) | ❌ | ✅ | ❌ | ✅ |
|
||||
| Create linked | ✅ (CustomFieldValueCreateScope) | ❌ | ✅ | ❌ | ✅ |
|
||||
| Destroy linked | ✅ (scope :linked) | ❌ | ✅ | ❌ | ✅ |
|
||||
| Read all | ❌ | ✅ | ✅ | ✅ | ✅ |
|
||||
| Create | ❌ | ❌ | ✅ | ❌ | ✅ |
|
||||
| Destroy | ❌ | ❌ | ✅ | ❌ | ✅ |
|
||||
| Create all | ❌ | ❌ | ✅ | ❌ | ✅ |
|
||||
| Destroy all | ❌ | ❌ | ✅ | ❌ | ✅ |
|
||||
|
||||
### CustomField Resource Policies
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue