- M2: get_user_by_email/1 uses lower(email) = lower(?) so User@Example.com and user@example.com resolve to the same account; closes a lookalike- registration / password-reset confusion risk. - M8: webhook auth plug no longer echoes "Webhook authentication not configured" — the misconfiguration is logged server-side and the caller gets a generic Internal server error so endpoints can't be probed. - M9: coverages controller logs the underlying KMZ build error and returns a generic "Failed to build KMZ" string — filesystem paths and zip internals no longer leak. - M10: account-data controller no longer crashes with MatchError when an org has zero or multiple memberships; defaults to "member" and treats any owner membership as owner. - M11: ActivityController switches to a hard whitelist (Atom.to_string/1 comparison) instead of String.to_existing_atom/1; unknown filter types are dropped silently and the atom table can't be grown from API input. - M16: title-tracking MutationObserver is stored on `this` so destroyed() actually disconnects it — fixes a memory leak per LV navigation.
57 lines
1.5 KiB
Elixir
57 lines
1.5 KiB
Elixir
defmodule ToweropsWeb.Plugs.WebhookAuth do
|
|
@moduledoc """
|
|
Plug for authenticating webhook requests using a shared secret.
|
|
|
|
Expects the Authorization header to contain a Bearer token matching
|
|
the configured `agent_webhook_secret`.
|
|
|
|
Returns 401 Unauthorized if:
|
|
- No Authorization header is present
|
|
- Token does not match the configured secret
|
|
"""
|
|
|
|
import Phoenix.Controller, only: [json: 2]
|
|
import Plug.Conn
|
|
|
|
def init(opts), do: opts
|
|
|
|
def call(conn, _opts) do
|
|
case get_req_header(conn, "authorization") do
|
|
["Bearer " <> token] ->
|
|
verify_secret(conn, token)
|
|
|
|
_ ->
|
|
conn
|
|
|> put_status(:unauthorized)
|
|
|> json(%{error: "Missing or invalid Authorization header"})
|
|
|> halt()
|
|
end
|
|
end
|
|
|
|
defp verify_secret(conn, token) do
|
|
secret = Application.get_env(:towerops, :agent_webhook_secret)
|
|
|
|
cond do
|
|
is_nil(secret) or secret == "" ->
|
|
require Logger
|
|
|
|
Logger.error("agent_webhook_secret not configured — rejecting webhook request")
|
|
|
|
# Log the real reason server-side; don't echo it to the caller —
|
|
# otherwise an attacker can probe for unconfigured endpoints.
|
|
conn
|
|
|> put_status(:internal_server_error)
|
|
|> json(%{error: "Internal server error"})
|
|
|> halt()
|
|
|
|
Plug.Crypto.secure_compare(token, secret) ->
|
|
conn
|
|
|
|
true ->
|
|
conn
|
|
|> put_status(:unauthorized)
|
|
|> json(%{error: "Invalid webhook secret"})
|
|
|> halt()
|
|
end
|
|
end
|
|
end
|