defmodule TarakanWeb.AdminLive.Show do use TarakanWeb, :live_view alias Tarakan.Accounts alias Tarakan.Accounts.Account alias Tarakan.Policy alias TarakanWeb.AccountAuth @impl true def mount(%{"id" => id}, _session, socket) do scope = socket.assigns.current_scope return_to = ~p"/admin/accounts/#{id}" cond do not Policy.allowed?(scope, :administer) -> {:ok, unauthorized(socket)} not Accounts.sudo_mode?(scope.account) -> {:ok, socket |> put_flash(:error, "Confirm it's you before changing account authority.") |> redirect(to: AccountAuth.reauth_path(return_to))} true -> case Accounts.get_account_for_admin(scope, id) do {:ok, account} -> {:ok, assign_account(socket, account)} {:error, _reason} -> {:ok, unauthorized(socket)} end end end @impl true def handle_event("validate", %{"authorization" => params}, socket) do form = socket.assigns.account |> Account.authorization_changeset(params) |> Map.put(:action, :validate) |> to_form(as: :authorization) {:noreply, assign(socket, :authorization_form, form)} end def handle_event("save", %{"authorization" => params}, socket) do with_recent_auth(socket, fn -> case Accounts.update_authorization( socket.assigns.current_scope, socket.assigns.account, params ) do {:ok, account} -> {:noreply, socket |> assign_account(account) |> put_flash(:info, "Authorization updated for @#{account.handle}.")} {:error, :last_admin} -> {:noreply, socket |> assign_invalid_form(params) |> put_flash(:error, "The last active administrator cannot be demoted or restricted.")} {:error, %Ecto.Changeset{} = changeset} -> {:noreply, assign(socket, :authorization_form, to_form(changeset, as: :authorization))} {:error, _reason} -> {:noreply, unauthorized(socket)} end end) end def handle_event(_event, _params, socket), do: {:noreply, socket} # The mount-time sudo check only covers the initial render; a long-lived # LiveView can outlast the two-hour window, so mutating events re-check. defp with_recent_auth(socket, fun) do if Accounts.sudo_mode?(socket.assigns.current_scope.account) do fun.() else return_to = ~p"/admin/accounts/#{socket.assigns.account.id}" {:noreply, socket |> put_flash(:error, "Confirm it's you before changing account authority.") |> push_navigate(to: AccountAuth.reauth_path(return_to))} end end defp assign_account(socket, account) do socket |> assign(:page_title, "Manage @#{account.handle}") |> assign(:account, account) |> assign( :authorization_form, to_form(Account.authorization_changeset(account, %{}), as: :authorization) ) end defp assign_invalid_form(socket, params) do changeset = socket.assigns.account |> Account.authorization_changeset(params) |> Ecto.Changeset.add_error(:platform_role, "must leave at least one active administrator") assign(socket, :authorization_form, to_form(changeset, as: :authorization)) end defp unauthorized(socket) do socket |> put_flash(:error, "Administrator access is required.") |> redirect(to: ~p"/") end end