MembershipFeeCycle: own_data read :linked via bypass and HasPermission scope
- own_data gets read scope :linked; apply_scope in HasPermission; bypass check for own_data. - PermissionSetsTest expects own_data :linked, others :all for MFC read.
This commit is contained in:
parent
890a4d3752
commit
178f5a01c7
6 changed files with 140 additions and 6 deletions
|
|
@ -84,7 +84,13 @@ defmodule Mv.MembershipFees.MembershipFeeCycle do
|
|||
end
|
||||
end
|
||||
|
||||
# READ: bypass for own_data (:linked) then HasPermission for :all; create/update/destroy: HasPermission only.
|
||||
policies do
|
||||
bypass action_type(:read) do
|
||||
description "own_data: read only cycles where member_id == actor.member_id"
|
||||
authorize_if Mv.Authorization.Checks.MembershipFeeCycleReadLinkedForOwnData
|
||||
end
|
||||
|
||||
policy action_type([:read, :create, :update, :destroy]) do
|
||||
description "Check permissions from role (all read; normal_user and admin create/update/destroy)"
|
||||
authorize_if Mv.Authorization.Checks.HasPermission
|
||||
|
|
|
|||
|
|
@ -351,6 +351,10 @@ defmodule Mv.Authorization.Checks.HasPermission do
|
|||
# MemberGroup.member_id → Member.id → User.member_id (own linked member's group associations)
|
||||
linked_filter_by_member_id(actor, :member_id)
|
||||
|
||||
"MembershipFeeCycle" ->
|
||||
# MembershipFeeCycle.member_id → Member.id → User.member_id (own linked member's cycles)
|
||||
linked_filter_by_member_id(actor, :member_id)
|
||||
|
||||
_ ->
|
||||
# Fallback for other resources
|
||||
{:filter, expr(user_id == ^actor.id)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
defmodule Mv.Authorization.Checks.MembershipFeeCycleReadLinkedForOwnData do
|
||||
@moduledoc """
|
||||
Policy check for MembershipFeeCycle read: true only when actor has permission set "own_data"
|
||||
AND record.member_id == actor.member_id.
|
||||
|
||||
Used in a bypass so that own_data gets the linked filter (via auto_filter for list queries),
|
||||
while admin with member_id does not match and gets :all from HasPermission.
|
||||
|
||||
- With a record (e.g. get by id): returns true only when own_data and member_id match.
|
||||
- Without a record (list query): return :unknown so authorizer applies auto_filter.
|
||||
"""
|
||||
use Ash.Policy.Check
|
||||
|
||||
alias Mv.Authorization.Checks.ActorPermissionSetIs
|
||||
|
||||
@impl true
|
||||
def type, do: :filter
|
||||
|
||||
@impl true
|
||||
def describe(_opts),
|
||||
do: "own_data can read only membership_fee_cycles where member_id == actor.member_id"
|
||||
|
||||
@impl true
|
||||
def strict_check(actor, authorizer, _opts) do
|
||||
record = get_record_from_authorizer(authorizer)
|
||||
is_own_data = ActorPermissionSetIs.match?(actor, authorizer, permission_set_name: "own_data")
|
||||
|
||||
cond do
|
||||
is_nil(record) and is_own_data ->
|
||||
{:ok, :unknown}
|
||||
|
||||
is_nil(record) ->
|
||||
{:ok, false}
|
||||
|
||||
not is_own_data ->
|
||||
{:ok, false}
|
||||
|
||||
record.member_id == actor.member_id ->
|
||||
{:ok, true}
|
||||
|
||||
true ->
|
||||
{:ok, false}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def auto_filter(actor, _authorizer, _opts) do
|
||||
if ActorPermissionSetIs.match?(actor, nil, permission_set_name: "own_data") &&
|
||||
Map.get(actor, :member_id) do
|
||||
[member_id: actor.member_id]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp get_record_from_authorizer(authorizer) do
|
||||
case authorizer.subject do
|
||||
%{data: data} when not is_nil(data) -> data
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -129,7 +129,7 @@ defmodule Mv.Authorization.PermissionSets do
|
|||
group_read_all() ++
|
||||
[perm("MemberGroup", :read, :linked)] ++
|
||||
membership_fee_type_read_all() ++
|
||||
membership_fee_cycle_read_all(),
|
||||
[perm("MembershipFeeCycle", :read, :linked)],
|
||||
pages: [
|
||||
# No "/" - Mitglied must not see member index at root (same content as /members).
|
||||
# Own profile (sidebar links to /users/:id) and own user edit
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue