System Actor Mode for Systemic Flows closes #348 #361
3 changed files with 27 additions and 39 deletions
|
|
@ -41,10 +41,8 @@ defmodule Mv.EmailSync.Changes.SyncMemberEmailToUser do
|
||||||
Ash.Changeset.around_transaction(changeset, fn cs, callback ->
|
Ash.Changeset.around_transaction(changeset, fn cs, callback ->
|
||||||
result = callback.(cs)
|
result = callback.(cs)
|
||||||
|
|
||||||
actor = Map.get(changeset.context, :actor)
|
|
||||||
|
|
||||||
with {:ok, member} <- Helpers.extract_record(result),
|
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)
|
Helpers.sync_email_to_linked_record(result, linked_user, new_email)
|
||||||
else
|
else
|
||||||
_ -> result
|
_ -> result
|
||||||
|
|
|
||||||
|
|
@ -33,17 +33,7 @@ defmodule Mv.EmailSync.Changes.SyncUserEmailToMember do
|
||||||
if Map.get(context, :syncing_email, false) do
|
if Map.get(context, :syncing_email, false) do
|
||||||
changeset
|
changeset
|
||||||
else
|
else
|
||||||
# Ensure actor is in changeset context - get it from context if available
|
sync_email(changeset)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -52,7 +42,7 @@ defmodule Mv.EmailSync.Changes.SyncUserEmailToMember do
|
||||||
result = callback.(cs)
|
result = callback.(cs)
|
||||||
|
|
||||||
with {:ok, record} <- Helpers.extract_record(result),
|
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 Member-side, we need to update the member in the result
|
||||||
# When called from User-side, we update the linked member in DB only
|
# When called from User-side, we update the linked member in DB only
|
||||||
case record do
|
case record do
|
||||||
|
|
@ -71,19 +61,16 @@ defmodule Mv.EmailSync.Changes.SyncUserEmailToMember do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Retrieves user and member - works for both resource types
|
# Retrieves user and member - works for both resource types
|
||||||
defp get_user_and_member(%Mv.Accounts.User{} = user, changeset) do
|
# Uses system actor via Loader functions
|
||||||
actor = Map.get(changeset.context, :actor)
|
defp get_user_and_member(%Mv.Accounts.User{} = user) do
|
||||||
|
case Loader.get_linked_member(user) do
|
||||||
case Loader.get_linked_member(user, actor) do
|
|
||||||
nil -> {:error, :no_member}
|
nil -> {:error, :no_member}
|
||||||
member -> {:ok, user, member}
|
member -> {:ok, user, member}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_user_and_member(%Mv.Membership.Member{} = member, changeset) do
|
defp get_user_and_member(%Mv.Membership.Member{} = member) do
|
||||||
actor = Map.get(changeset.context, :actor)
|
case Loader.load_linked_user!(member) do
|
||||||
|
|
||||||
case Loader.load_linked_user!(member, actor) do
|
|
||||||
{:ok, user} -> {:ok, user, member}
|
{:ok, user} -> {:ok, user, member}
|
||||||
error -> error
|
error -> error
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,26 @@ defmodule Mv.EmailSync.Loader do
|
||||||
|
|
||||||
## Authorization
|
## Authorization
|
||||||
|
|
||||||
This module runs systemically and accepts optional actor parameters.
|
This module runs systemically and uses the system actor for all operations.
|
||||||
When called from hooks/changes, actor is extracted from changeset context.
|
This ensures that email synchronization always works, regardless of user permissions.
|
||||||
When called directly, actor should be provided for proper authorization.
|
|
||||||
|
|
||||||
All functions accept an optional `actor` parameter that is passed to Ash operations
|
All functions use `Mv.Helpers.SystemActor.get_system_actor/0` to bypass
|
||||||
to ensure proper authorization checks are performed.
|
user permission checks, as email sync is a mandatory side effect.
|
||||||
"""
|
"""
|
||||||
alias Mv.Helpers
|
alias Mv.Helpers
|
||||||
|
alias Mv.Helpers.SystemActor
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Loads the member linked to a user, returns nil if not linked or on error.
|
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(user)
|
||||||
def get_linked_member(%{member_id: nil}, _actor), do: nil
|
def get_linked_member(%{member_id: nil}), do: nil
|
||||||
|
|
||||||
def get_linked_member(%{member_id: id}, actor) do
|
def get_linked_member(%{member_id: id}) do
|
||||||
opts = Helpers.ash_actor_opts(actor)
|
system_actor = SystemActor.get_system_actor()
|
||||||
|
opts = Helpers.ash_actor_opts(system_actor)
|
||||||
|
|
||||||
case Ash.get(Mv.Membership.Member, id, opts) do
|
case Ash.get(Mv.Membership.Member, id, opts) do
|
||||||
{:ok, member} -> member
|
{:ok, member} -> member
|
||||||
|
|
@ -34,10 +35,11 @@ defmodule Mv.EmailSync.Loader do
|
||||||
@doc """
|
@doc """
|
||||||
Loads the user linked to a member, returns nil if not linked or on error.
|
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
|
def get_linked_user(member) do
|
||||||
opts = Helpers.ash_actor_opts(actor)
|
system_actor = SystemActor.get_system_actor()
|
||||||
|
opts = Helpers.ash_actor_opts(system_actor)
|
||||||
|
|
||||||
case Ash.load(member, :user, opts) do
|
case Ash.load(member, :user, opts) do
|
||||||
{:ok, %{user: user}} -> user
|
{: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.
|
Loads the user linked to a member, returning an error tuple if not linked.
|
||||||
Useful when a link is required for the operation.
|
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
|
def load_linked_user!(member) do
|
||||||
opts = Helpers.ash_actor_opts(actor)
|
system_actor = SystemActor.get_system_actor()
|
||||||
|
opts = Helpers.ash_actor_opts(system_actor)
|
||||||
|
|
||||||
case Ash.load(member, :user, opts) do
|
case Ash.load(member, :user, opts) do
|
||||||
{:ok, %{user: user}} when not is_nil(user) -> {:ok, user}
|
{:ok, %{user: user}} when not is_nil(user) -> {:ok, user}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue