From ba5c982368473a72bd1113a353d2e9a684130b1b Mon Sep 17 00:00:00 2001 From: Moritz Date: Sat, 24 Jan 2026 01:42:15 +0100 Subject: [PATCH] Use authorize?: false for integrity checks in validations --- lib/membership/member.ex | 27 +++++++--------------- lib/membership_fees/membership_fee_type.ex | 18 +++++---------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 0a14efe..f2f27c0 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -393,25 +393,11 @@ defmodule Mv.Membership.Member do user_id = user_arg[:id] current_member_id = changeset.data.id - # Get actor from changeset context for authorization - # Use system_actor as fallback if no actor is present (for systemic operations) - actor = - case Map.get(changeset.context || %{}, :actor) do - nil -> Mv.Helpers.SystemActor.get_system_actor() - actor -> actor - end - - # Check the current state of the user in the database - # Check if authorization is disabled in the parent operation's context - # Access private context where authorize? flag is stored - authorize? = - case get_in(changeset.context, [:private, :authorize?]) do - false -> false - _ -> true - end - - # Pass actor and authorize? to ensure proper authorization (User might have policies in future) - case Ash.get(Mv.Accounts.User, user_id, actor: actor, authorize?: authorize?) do + # This is an integrity check, not a user authorization check + # Use authorize?: false to bypass policies for this internal validation query + # This ensures the validation always works regardless of actor availability + # (consistent with MembershipFeeType destroy validations) + case Ash.get(Mv.Accounts.User, user_id, authorize?: false) do # User is free to be linked {:ok, %{member_id: nil}} -> :ok @@ -424,6 +410,9 @@ defmodule Mv.Membership.Member do # User is linked to a different member - prevent "stealing" {:error, field: :user, message: "User is already linked to another member"} + {:error, %Ash.Error.Query.NotFound{}} -> + {:error, field: :user, message: "User not found"} + {:error, _} -> {:error, field: :user, message: "User not found"} end diff --git a/lib/membership_fees/membership_fee_type.ex b/lib/membership_fees/membership_fee_type.ex index 64ca8f9..498ff75 100644 --- a/lib/membership_fees/membership_fee_type.ex +++ b/lib/membership_fees/membership_fee_type.ex @@ -85,13 +85,11 @@ defmodule Mv.MembershipFees.MembershipFeeType do if changeset.action_type == :destroy do require Ash.Query - # Use system_actor for validation queries (systemic operation) - system_actor = Mv.Helpers.SystemActor.get_system_actor() - + # Integrity check: count members without authorization (systemic operation) member_count = Mv.Membership.Member |> Ash.Query.filter(membership_fee_type_id == ^changeset.data.id) - |> Ash.count!(actor: system_actor) + |> Ash.count!(authorize?: false) if member_count > 0 do {:error, @@ -111,13 +109,11 @@ defmodule Mv.MembershipFees.MembershipFeeType do if changeset.action_type == :destroy do require Ash.Query - # Use system_actor for validation queries (systemic operation) - system_actor = Mv.Helpers.SystemActor.get_system_actor() - + # Integrity check: count cycles without authorization (systemic operation) cycle_count = Mv.MembershipFees.MembershipFeeCycle |> Ash.Query.filter(membership_fee_type_id == ^changeset.data.id) - |> Ash.count!(actor: system_actor) + |> Ash.count!(authorize?: false) if cycle_count > 0 do {:error, @@ -137,13 +133,11 @@ defmodule Mv.MembershipFees.MembershipFeeType do if changeset.action_type == :destroy do require Ash.Query - # Use system_actor for validation queries (systemic operation) - system_actor = Mv.Helpers.SystemActor.get_system_actor() - + # Integrity check: count settings without authorization (systemic operation) setting_count = Mv.Membership.Setting |> Ash.Query.filter(default_membership_fee_type_id == ^changeset.data.id) - |> Ash.count!(actor: system_actor) + |> Ash.count!(authorize?: false) if setting_count > 0 do {:error,