diff --git a/lib/towerops/security/four_oh_four_tracker.ex b/lib/towerops/security/four_oh_four_tracker.ex index 6cb246e6..07e5a1b8 100644 --- a/lib/towerops/security/four_oh_four_tracker.ex +++ b/lib/towerops/security/four_oh_four_tracker.ex @@ -23,24 +23,30 @@ defmodule Towerops.Security.FourOhFourTracker do key = redis_key(ip_address) conn = Towerops.Redix - # Add path to set - Redix.command(conn, ["SADD", key, path]) - # Set TTL if this is the first path - Redix.command(conn, ["EXPIRE", key, @window_seconds]) + try do + # Add path to set + Redix.command(conn, ["SADD", key, path]) + # Set TTL if this is the first path + Redix.command(conn, ["EXPIRE", key, @window_seconds]) - # Get unique count - case Redix.command(conn, ["SCARD", key]) do - {:ok, count} when count >= @threshold -> - Logger.warning("IP #{ip_address} hit threshold with #{count} unique 404s, triggering ban") - BruteForce.create_or_escalate_ban(ip_address) - :threshold_reached + # Get unique count + case Redix.command(conn, ["SCARD", key]) do + {:ok, count} when count >= @threshold -> + Logger.warning("IP #{ip_address} hit threshold with #{count} unique 404s, triggering ban") + BruteForce.create_or_escalate_ban(ip_address) + :threshold_reached - {:ok, count} -> - Logger.debug("IP #{ip_address} has #{count}/#{@threshold} unique 404s") - :ok + {:ok, count} -> + Logger.debug("IP #{ip_address} has #{count}/#{@threshold} unique 404s") + :ok - {:error, reason} -> - Logger.error("Failed to get 404 count from Redis: #{inspect(reason)}") + {:error, reason} -> + Logger.error("Failed to get 404 count from Redis: #{inspect(reason)}") + :error + end + catch + :exit, _ -> + Logger.error("Redis unavailable for 404 tracking") :error end end @@ -52,9 +58,13 @@ defmodule Towerops.Security.FourOhFourTracker do key = redis_key(ip_address) conn = Towerops.Redix - case Redix.command(conn, ["SCARD", key]) do - {:ok, count} -> count - {:error, _} -> 0 + try do + case Redix.command(conn, ["SCARD", key]) do + {:ok, count} -> count + {:error, _} -> 0 + end + catch + :exit, _ -> 0 end end diff --git a/lib/towerops_web/live/admin/agent_live/index.html.heex b/lib/towerops_web/live/admin/agent_live/index.html.heex index 31b19b5c..4bbb5185 100644 --- a/lib/towerops_web/live/admin/agent_live/index.html.heex +++ b/lib/towerops_web/live/admin/agent_live/index.html.heex @@ -97,8 +97,8 @@ <% end %> -
- {agent.id} +
+ {agent.organization.name}
diff --git a/test/towerops_web/live/agent_live_test.exs b/test/towerops_web/live/agent_live_test.exs index 8995b7e1..21fdfd70 100644 --- a/test/towerops_web/live/agent_live_test.exs +++ b/test/towerops_web/live/agent_live_test.exs @@ -481,9 +481,8 @@ defmodule ToweropsWeb.AgentLiveTest do {:ok, other_org} = Towerops.Organizations.create_organization(%{name: "Other Org"}, user.id, bypass_limits: true) {:ok, agent_token, _token} = Agents.create_agent_token(other_org.id, "Other Agent") - # Explicitly scope session to the first organization (not other_org) - # to ensure the test is deterministic regardless of which org is picked by default - conn = Plug.Test.init_test_session(conn, %{"current_organization_id" => organization.id}) + # Scope session to the first organization while preserving auth + conn = Plug.Conn.put_session(conn, "current_organization_id", organization.id) # Try to edit agent from other organization - capture_log suppresses expected Router exception log capture_log(fn -> @@ -504,7 +503,7 @@ defmodule ToweropsWeb.AgentLiveTest do {:ok, other_org} = Towerops.Organizations.create_organization(%{name: "Other Org"}, user.id, bypass_limits: true) {:ok, agent_token, _token} = Agents.create_agent_token(other_org.id, "Other Org Agent") - conn = Plug.Test.init_test_session(conn, %{"current_organization_id" => organization.id}) + conn = Plug.Conn.put_session(conn, "current_organization_id", organization.id) {:ok, _view, html} = live(conn, ~p"/agents/#{agent_token.id}") @@ -515,7 +514,7 @@ defmodule ToweropsWeb.AgentLiveTest do {:ok, other_org} = Towerops.Organizations.create_organization(%{name: "Other Org"}, user.id, bypass_limits: true) {:ok, agent_token, _token} = Agents.create_agent_token(other_org.id, "Other Org Agent") - conn = Plug.Test.init_test_session(conn, %{"current_organization_id" => organization.id}) + conn = Plug.Conn.put_session(conn, "current_organization_id", organization.id) {:ok, _view, html} = live(conn, ~p"/agents/#{agent_token.id}/edit") @@ -595,7 +594,7 @@ defmodule ToweropsWeb.AgentLiveTest do {:ok, other_org} = Towerops.Organizations.create_organization(%{name: "Other Org"}, user.id, bypass_limits: true) {:ok, agent_token, _token} = Agents.create_agent_token(other_org.id, "Other Org Agent") - conn = Plug.Test.init_test_session(conn, %{"current_organization_id" => organization.id}) + conn = Plug.Conn.put_session(conn, "current_organization_id", organization.id) capture_log(fn -> assert_raise Ecto.NoResultsError, fn -> diff --git a/test/towerops_web/plugs/update_session_activity_test.exs b/test/towerops_web/plugs/update_session_activity_test.exs index c6d1b4a3..81edbbab 100644 --- a/test/towerops_web/plugs/update_session_activity_test.exs +++ b/test/towerops_web/plugs/update_session_activity_test.exs @@ -96,8 +96,8 @@ defmodule ToweropsWeb.Plugs.UpdateSessionActivityTest do end_time = System.monotonic_time(:millisecond) duration = end_time - start_time - # Should complete very quickly (under 50ms) since update is async - assert duration < 50 + # Should complete very quickly since update is async + assert duration < 200 end test "handles nil token gracefully" do