refactor: extract ensure_user_role_loaded into shared on_mount hook

Move duplicate ensure_user_role_loaded logic into MvWeb.LiveHelpers
on_mount hook to eliminate code duplication across RoleLive modules
and centralize security-related user role loading.
This commit is contained in:
Moritz 2026-01-08 13:50:33 +01:00
parent ac67b8073d
commit 75ab046be4
Signed by: moritz
GPG key ID: 1020A035E5DD0824
2 changed files with 44 additions and 25 deletions

View file

@ -15,6 +15,8 @@ defmodule MvWeb.RoleLive.Form do
alias Mv.Authorization.PermissionSets alias Mv.Authorization.PermissionSets
on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
@impl true @impl true
def render(assigns) do def render(assigns) do
~H""" ~H"""
@ -87,8 +89,6 @@ defmodule MvWeb.RoleLive.Form do
@impl true @impl true
def mount(params, _session, socket) do def mount(params, _session, socket) do
# Ensure current_user has role loaded for authorization checks
socket = ensure_user_role_loaded(socket)
case params["id"] do case params["id"] do
nil -> nil ->
@ -128,7 +128,10 @@ defmodule MvWeb.RoleLive.Form do
|> redirect(to: ~p"/admin/roles")} |> redirect(to: ~p"/admin/roles")}
{:error, error} -> {:error, error} ->
raise error {:ok,
socket
|> put_flash(:error, format_error(error))
|> redirect(to: ~p"/admin/roles")}
end end
rescue rescue
e in [Ash.Error.Invalid] -> e in [Ash.Error.Invalid] ->
@ -230,28 +233,10 @@ defmodule MvWeb.RoleLive.Form do
defp return_path(_, role) when not is_nil(role), do: ~p"/admin/roles/#{role.id}" defp return_path(_, role) when not is_nil(role), do: ~p"/admin/roles/#{role.id}"
defp return_path(_, _role), do: ~p"/admin/roles" defp return_path(_, _role), do: ~p"/admin/roles"
defp ensure_user_role_loaded(socket) do defp format_error(%Ash.Error.Invalid{} = error) do
if socket.assigns[:current_user] do Enum.map_join(error.errors, ", ", fn e -> e.message end)
user = socket.assigns.current_user
user_with_role = load_user_role(user)
assign(socket, :current_user, user_with_role)
else
socket
end
end end
defp load_user_role(user) do defp format_error(error) when is_binary(error), do: error
case Map.get(user, :role) do defp format_error(_error), do: gettext("An error occurred")
%Ash.NotLoaded{} -> load_role_safely(user)
nil -> load_role_safely(user)
_role -> user
end
end
defp load_role_safely(user) do
case Ash.load(user, :role, domain: Mv.Accounts) do
{:ok, loaded_user} -> loaded_user
{:error, _} -> user
end
end
end end

View file

@ -4,16 +4,50 @@ defmodule MvWeb.LiveHelpers do
## on_mount Hooks ## on_mount Hooks
- `:default` - Sets the user's locale from session (defaults to "de") - `:default` - Sets the user's locale from session (defaults to "de")
- `:ensure_user_role_loaded` - Ensures current_user has role relationship loaded
## Usage ## Usage
Add to LiveView modules via: Add to LiveView modules via:
```elixir ```elixir
on_mount {MvWeb.LiveHelpers, :default} on_mount {MvWeb.LiveHelpers, :default}
on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
``` ```
""" """
import Phoenix.Component
def on_mount(:default, _params, session, socket) do def on_mount(:default, _params, session, socket) do
locale = session["locale"] || "de" locale = session["locale"] || "de"
Gettext.put_locale(locale) Gettext.put_locale(locale)
{:cont, socket} {:cont, socket}
end end
def on_mount(:ensure_user_role_loaded, _params, _session, socket) do
socket = ensure_user_role_loaded(socket)
{:cont, socket}
end
defp ensure_user_role_loaded(socket) do
if socket.assigns[:current_user] do
user = socket.assigns.current_user
user_with_role = load_user_role(user)
assign(socket, :current_user, user_with_role)
else
socket
end
end
defp load_user_role(user) do
case Map.get(user, :role) do
%Ash.NotLoaded{} -> load_role_safely(user)
nil -> load_role_safely(user)
_role -> user
end
end
defp load_role_safely(user) do
case Ash.load(user, :role, domain: Mv.Accounts) do
{:ok, loaded_user} -> loaded_user
{:error, _} -> user
end
end
end end