From 806b293eadf16118a506226bd3aca20a1e79fbdb Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Tue, 6 Jan 2026 13:22:13 -0600 Subject: [PATCH] Fix impersonation to show correct user data and banner Two issues were preventing impersonation from working correctly: 1. Templates were not passing current_scope to Layouts.authenticated, so the impersonation banner never displayed. 2. fetch_current_scope_for_user was looking up the user from the session token, which always pointed to the superuser. This caused the superuser to see their own organizations and equipment instead of the impersonated user's data. Changes: - Pass current_scope={@current_scope} to all Layouts.authenticated calls - Store target_user_id in session during impersonation - Fetch target user directly from database using target_user_id - Update both fetch_current_scope_for_user (for controllers) and mount_current_scope (for LiveViews) to properly handle impersonation - Clean up target_user_id from session when impersonation ends Now when a superuser impersonates a user, they correctly see: - The impersonation banner at the top with exit link - The target user's organizations and equipment - All data scoped to the impersonated user --- .../user_settings_html/edit.html.heex | 2 +- .../live/alert_live/index.html.heex | 1 + .../live/dashboard_live.html.heex | 1 + .../live/equipment_live/form.html.heex | 6 ++- .../live/equipment_live/index.html.heex | 1 + .../live/equipment_live/show.html.heex | 1 + .../live/graph_live/show.html.heex | 1 + .../live/org_live/index.html.heex | 2 +- lib/towerops_web/live/org_live/new.html.heex | 2 +- .../live/site_live/form.html.heex | 6 ++- .../live/site_live/index.html.heex | 1 + .../live/site_live/show.html.heex | 1 + lib/towerops_web/user_auth.ex | 37 ++++++++++++++----- 13 files changed, 47 insertions(+), 15 deletions(-) diff --git a/lib/towerops_web/controllers/user_settings_html/edit.html.heex b/lib/towerops_web/controllers/user_settings_html/edit.html.heex index cf10c894..e1543096 100644 --- a/lib/towerops_web/controllers/user_settings_html/edit.html.heex +++ b/lib/towerops_web/controllers/user_settings_html/edit.html.heex @@ -1,4 +1,4 @@ - +
<.header> Account Settings diff --git a/lib/towerops_web/live/alert_live/index.html.heex b/lib/towerops_web/live/alert_live/index.html.heex index 81febf60..abb73e0f 100644 --- a/lib/towerops_web/live/alert_live/index.html.heex +++ b/lib/towerops_web/live/alert_live/index.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/live/dashboard_live.html.heex b/lib/towerops_web/live/dashboard_live.html.heex index 0d3eb052..80f6c8f5 100644 --- a/lib/towerops_web/live/dashboard_live.html.heex +++ b/lib/towerops_web/live/dashboard_live.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/live/equipment_live/form.html.heex b/lib/towerops_web/live/equipment_live/form.html.heex index 5ab4557b..44742083 100644 --- a/lib/towerops_web/live/equipment_live/form.html.heex +++ b/lib/towerops_web/live/equipment_live/form.html.heex @@ -1,4 +1,8 @@ - +
<.link navigate={ diff --git a/lib/towerops_web/live/equipment_live/index.html.heex b/lib/towerops_web/live/equipment_live/index.html.heex index 6638bd8e..7092c102 100644 --- a/lib/towerops_web/live/equipment_live/index.html.heex +++ b/lib/towerops_web/live/equipment_live/index.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/live/equipment_live/show.html.heex b/lib/towerops_web/live/equipment_live/show.html.heex index d521df4a..8dc2d3be 100644 --- a/lib/towerops_web/live/equipment_live/show.html.heex +++ b/lib/towerops_web/live/equipment_live/show.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/live/graph_live/show.html.heex b/lib/towerops_web/live/graph_live/show.html.heex index a7ff5a16..e4b1ffde 100644 --- a/lib/towerops_web/live/graph_live/show.html.heex +++ b/lib/towerops_web/live/graph_live/show.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/live/org_live/index.html.heex b/lib/towerops_web/live/org_live/index.html.heex index a951f462..a37963ae 100644 --- a/lib/towerops_web/live/org_live/index.html.heex +++ b/lib/towerops_web/live/org_live/index.html.heex @@ -1,4 +1,4 @@ - + <.header> {@page_title} <:actions> diff --git a/lib/towerops_web/live/org_live/new.html.heex b/lib/towerops_web/live/org_live/new.html.heex index c6d3d70a..819375fb 100644 --- a/lib/towerops_web/live/org_live/new.html.heex +++ b/lib/towerops_web/live/org_live/new.html.heex @@ -1,4 +1,4 @@ - + <.header> {@page_title} <:subtitle>Create a new organization to manage your sites and equipment diff --git a/lib/towerops_web/live/site_live/form.html.heex b/lib/towerops_web/live/site_live/form.html.heex index d4da27f7..5e6fef4b 100644 --- a/lib/towerops_web/live/site_live/form.html.heex +++ b/lib/towerops_web/live/site_live/form.html.heex @@ -1,4 +1,8 @@ - +
<.link navigate={ diff --git a/lib/towerops_web/live/site_live/index.html.heex b/lib/towerops_web/live/site_live/index.html.heex index 313df3e5..baa50588 100644 --- a/lib/towerops_web/live/site_live/index.html.heex +++ b/lib/towerops_web/live/site_live/index.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/live/site_live/show.html.heex b/lib/towerops_web/live/site_live/show.html.heex index 769c5777..60e7099c 100644 --- a/lib/towerops_web/live/site_live/show.html.heex +++ b/lib/towerops_web/live/site_live/show.html.heex @@ -1,5 +1,6 @@ diff --git a/lib/towerops_web/user_auth.ex b/lib/towerops_web/user_auth.ex index a6f7ccd6..3e9224c3 100644 --- a/lib/towerops_web/user_auth.ex +++ b/lib/towerops_web/user_auth.ex @@ -72,16 +72,17 @@ defmodule ToweropsWeb.UserAuth do # Check if currently impersonating if get_session(conn, :impersonating) do superuser_id = get_session(conn, :superuser_id) + target_user_id = get_session(conn, :target_user_id) - with {token, conn} <- ensure_user_token(conn), - {user, _token_inserted_at} <- Accounts.get_user_by_session_token(token), - superuser when not is_nil(superuser) <- Accounts.get_user(superuser_id) do - assign(conn, :current_scope, Scope.for_impersonation(superuser, user)) + with superuser when not is_nil(superuser) <- Accounts.get_user(superuser_id), + target_user when not is_nil(target_user) <- Accounts.get_user(target_user_id) do + assign(conn, :current_scope, Scope.for_impersonation(superuser, target_user)) else _ -> # Impersonation invalid, clear it conn |> delete_session(:superuser_id) + |> delete_session(:target_user_id) |> delete_session(:impersonating) |> assign(:current_scope, Scope.for_user(nil)) end @@ -388,13 +389,27 @@ defmodule ToweropsWeb.UserAuth do defp mount_current_scope(socket, session) do Phoenix.Component.assign_new(socket, :current_scope, fn -> - if user_token = session["user_token"] do - case Accounts.get_user_by_session_token(user_token) do - {user, _token_inserted_at} -> Scope.for_user(user) - nil -> Scope.for_user(nil) + # Check if currently impersonating + if session["impersonating"] do + superuser_id = session["superuser_id"] + target_user_id = session["target_user_id"] + + with superuser when not is_nil(superuser) <- Accounts.get_user(superuser_id), + target_user when not is_nil(target_user) <- Accounts.get_user(target_user_id) do + Scope.for_impersonation(superuser, target_user) + else + _ -> Scope.for_user(nil) end else - Scope.for_user(nil) + # Normal authentication flow + if user_token = session["user_token"] do + case Accounts.get_user_by_session_token(user_token) do + {user, _token_inserted_at} -> Scope.for_user(user) + nil -> Scope.for_user(nil) + end + else + Scope.for_user(nil) + end end end) end @@ -431,9 +446,10 @@ defmodule ToweropsWeb.UserAuth do ip_address: ip }) - # Store superuser ID in session and update scope + # Store superuser ID and target user ID in session and update scope conn |> put_session(:superuser_id, superuser.id) + |> put_session(:target_user_id, target_user.id) |> put_session(:impersonating, true) |> assign(:current_scope, Scope.for_impersonation(superuser, target_user)) |> put_flash(:info, "Now impersonating #{target_user.email}") @@ -467,6 +483,7 @@ defmodule ToweropsWeb.UserAuth do # Restore superuser session conn |> delete_session(:superuser_id) + |> delete_session(:target_user_id) |> delete_session(:impersonating) |> assign(:current_scope, Scope.for_user(superuser)) |> put_flash(:info, "Stopped impersonating")