towerops/lib/towerops_web/plugs/agent_auth.ex

70 lines
1.7 KiB
Elixir

defmodule ToweropsWeb.Plugs.AgentAuth do
@moduledoc """
Plug for authenticating remote agent API requests.
Validates Bearer tokens in the Authorization header and assigns the agent token
to the connection. Also updates the agent's last_seen_at timestamp asynchronously.
"""
import Phoenix.Controller
import Plug.Conn
alias Ecto.Adapters.SQL.Sandbox
alias Towerops.Agents
@doc """
Initializes the plug with options.
"""
def init(opts), do: opts
@doc """
Validates the agent token from the Authorization header.
If valid, assigns :current_agent_token to the connection and updates heartbeat.
If invalid or missing, returns 401 Unauthorized and halts the connection.
"""
def call(conn, _opts) do
case get_req_header(conn, "authorization") do
["Bearer " <> token] ->
case Agents.verify_agent_token(token) do
{:ok, agent_token} ->
conn
|> assign(:current_agent_token, agent_token)
|> update_heartbeat(agent_token)
{:error, _} ->
unauthorized(conn)
end
_ ->
unauthorized(conn)
end
end
defp update_heartbeat(conn, agent_token) do
ip = to_string(:inet_parse.ntoa(conn.remote_ip))
parent = self()
_ =
Task.start(fn ->
# Allow this task to use the test database sandbox
_ =
if Application.get_env(:towerops, :sql_sandbox) do
Sandbox.allow(Towerops.Repo, parent, self())
end
_ = Agents.update_agent_token_heartbeat(agent_token.id, ip)
end)
conn
end
defp unauthorized(conn) do
conn
|> put_status(:unauthorized)
|> put_view(json: ToweropsWeb.ErrorJSON)
|> render(:"401")
|> halt()
end
end