POST /api/v1/webhooks/agent-release triggers all connected agents to self-update via their existing WebSocket channels. Authenticated with a shared secret (AGENT_WEBHOOK_SECRET env var). - Add ReleaseChecker.invalidate_cache/0 for fresh GitHub fetch - Add Agents.list_updatable_agents/0 (enabled + seen < 10min) - Add Agents.broadcast_mass_update/0 orchestration function - Add WebhookAuth plug with timing-safe secret comparison - Add AgentReleaseWebhookController with :webhook pipeline - Configure AGENT_WEBHOOK_SECRET in dev/test/runtime configs
67 lines
1.9 KiB
Elixir
67 lines
1.9 KiB
Elixir
defmodule ToweropsWeb.Plugs.WebhookAuthTest do
|
|
use ToweropsWeb.ConnCase, async: true
|
|
|
|
alias ToweropsWeb.Plugs.WebhookAuth
|
|
|
|
describe "call/2 with valid authorization" do
|
|
test "passes through with valid Bearer token", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer test-webhook-secret")
|
|
|> WebhookAuth.call([])
|
|
|
|
refute conn.halted
|
|
end
|
|
end
|
|
|
|
describe "call/2 with missing authorization" do
|
|
test "returns 401 when Authorization header is missing", %{conn: conn} do
|
|
conn = WebhookAuth.call(conn, [])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
assert json_response(conn, 401) == %{"error" => "Missing or invalid Authorization header"}
|
|
end
|
|
|
|
test "returns 401 when Authorization header is empty", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "")
|
|
|> WebhookAuth.call([])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
assert json_response(conn, 401) == %{"error" => "Missing or invalid Authorization header"}
|
|
end
|
|
end
|
|
|
|
describe "call/2 with invalid authorization" do
|
|
test "returns 401 when token does not match", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer wrong-secret")
|
|
|> WebhookAuth.call([])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
assert json_response(conn, 401) == %{"error" => "Invalid webhook secret"}
|
|
end
|
|
|
|
test "returns 401 when using Basic auth instead of Bearer", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Basic dGVzdDp0ZXN0")
|
|
|> WebhookAuth.call([])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
assert json_response(conn, 401) == %{"error" => "Missing or invalid Authorization header"}
|
|
end
|
|
end
|
|
|
|
describe "init/1" do
|
|
test "returns options unchanged" do
|
|
assert WebhookAuth.init([]) == []
|
|
end
|
|
end
|
|
end
|