diff --git a/lib/mv/email_sync/changes/sync_member_email_to_user.ex b/lib/mv/email_sync/changes/sync_member_email_to_user.ex index 0c0d8f7..48c7955 100644 --- a/lib/mv/email_sync/changes/sync_member_email_to_user.ex +++ b/lib/mv/email_sync/changes/sync_member_email_to_user.ex @@ -41,10 +41,8 @@ defmodule Mv.EmailSync.Changes.SyncMemberEmailToUser do Ash.Changeset.around_transaction(changeset, fn cs, callback -> result = callback.(cs) - actor = Map.get(changeset.context, :actor) - with {:ok, member} <- Helpers.extract_record(result), - linked_user <- Loader.get_linked_user(member, actor) do + linked_user <- Loader.get_linked_user(member) do Helpers.sync_email_to_linked_record(result, linked_user, new_email) else _ -> result diff --git a/lib/mv/email_sync/changes/sync_user_email_to_member.ex b/lib/mv/email_sync/changes/sync_user_email_to_member.ex index 54829a4..eb6770c 100644 --- a/lib/mv/email_sync/changes/sync_user_email_to_member.ex +++ b/lib/mv/email_sync/changes/sync_user_email_to_member.ex @@ -33,17 +33,7 @@ defmodule Mv.EmailSync.Changes.SyncUserEmailToMember do if Map.get(context, :syncing_email, false) do changeset else - # Ensure actor is in changeset context - get it from context if available - actor = Map.get(changeset.context, :actor) || Map.get(context, :actor) - - changeset_with_actor = - if actor && !Map.has_key?(changeset.context, :actor) do - Ash.Changeset.put_context(changeset, :actor, actor) - else - changeset - end - - sync_email(changeset_with_actor) + sync_email(changeset) end end @@ -52,7 +42,7 @@ defmodule Mv.EmailSync.Changes.SyncUserEmailToMember do result = callback.(cs) with {:ok, record} <- Helpers.extract_record(result), - {:ok, user, member} <- get_user_and_member(record, cs) do + {:ok, user, member} <- get_user_and_member(record) do # When called from Member-side, we need to update the member in the result # When called from User-side, we update the linked member in DB only case record do @@ -71,19 +61,16 @@ defmodule Mv.EmailSync.Changes.SyncUserEmailToMember do end # Retrieves user and member - works for both resource types - defp get_user_and_member(%Mv.Accounts.User{} = user, changeset) do - actor = Map.get(changeset.context, :actor) - - case Loader.get_linked_member(user, actor) do + # Uses system actor via Loader functions + defp get_user_and_member(%Mv.Accounts.User{} = user) do + case Loader.get_linked_member(user) do nil -> {:error, :no_member} member -> {:ok, user, member} end end - defp get_user_and_member(%Mv.Membership.Member{} = member, changeset) do - actor = Map.get(changeset.context, :actor) - - case Loader.load_linked_user!(member, actor) do + defp get_user_and_member(%Mv.Membership.Member{} = member) do + case Loader.load_linked_user!(member) do {:ok, user} -> {:ok, user, member} error -> error end diff --git a/lib/mv/email_sync/loader.ex b/lib/mv/email_sync/loader.ex index 91927fb..98f85df 100644 --- a/lib/mv/email_sync/loader.ex +++ b/lib/mv/email_sync/loader.ex @@ -5,25 +5,26 @@ defmodule Mv.EmailSync.Loader do ## Authorization - This module runs systemically and accepts optional actor parameters. - When called from hooks/changes, actor is extracted from changeset context. - When called directly, actor should be provided for proper authorization. + This module runs systemically and uses the system actor for all operations. + This ensures that email synchronization always works, regardless of user permissions. - All functions accept an optional `actor` parameter that is passed to Ash operations - to ensure proper authorization checks are performed. + All functions use `Mv.Helpers.SystemActor.get_system_actor/0` to bypass + user permission checks, as email sync is a mandatory side effect. """ alias Mv.Helpers + alias Mv.Helpers.SystemActor @doc """ Loads the member linked to a user, returns nil if not linked or on error. - Accepts optional actor for authorization. + Uses system actor for authorization to ensure email sync always works. """ - def get_linked_member(user, actor \\ nil) - def get_linked_member(%{member_id: nil}, _actor), do: nil + def get_linked_member(user) + def get_linked_member(%{member_id: nil}), do: nil - def get_linked_member(%{member_id: id}, actor) do - opts = Helpers.ash_actor_opts(actor) + def get_linked_member(%{member_id: id}) do + system_actor = SystemActor.get_system_actor() + opts = Helpers.ash_actor_opts(system_actor) case Ash.get(Mv.Membership.Member, id, opts) do {:ok, member} -> member @@ -34,10 +35,11 @@ defmodule Mv.EmailSync.Loader do @doc """ Loads the user linked to a member, returns nil if not linked or on error. - Accepts optional actor for authorization. + Uses system actor for authorization to ensure email sync always works. """ - def get_linked_user(member, actor \\ nil) do - opts = Helpers.ash_actor_opts(actor) + def get_linked_user(member) do + system_actor = SystemActor.get_system_actor() + opts = Helpers.ash_actor_opts(system_actor) case Ash.load(member, :user, opts) do {:ok, %{user: user}} -> user @@ -49,10 +51,11 @@ defmodule Mv.EmailSync.Loader do Loads the user linked to a member, returning an error tuple if not linked. Useful when a link is required for the operation. - Accepts optional actor for authorization. + Uses system actor for authorization to ensure email sync always works. """ - def load_linked_user!(member, actor \\ nil) do - opts = Helpers.ash_actor_opts(actor) + def load_linked_user!(member) do + system_actor = SystemActor.get_system_actor() + opts = Helpers.ash_actor_opts(system_actor) case Ash.load(member, :user, opts) do {:ok, %{user: user}} when not is_nil(user) -> {:ok, user}