49 lines
1.9 KiB
Elixir
49 lines
1.9 KiB
Elixir
defmodule BirdyChatWeb.Api.Server.Internal.Controller do
|
|
@moduledoc """
|
|
A controller for handling inter-server communication. It started off with using Erlang term
|
|
format instead of JSON as communication language but then I removed it for the following
|
|
reasons:
|
|
|
|
1. The messages are mostly binaries anyway, there is no big efficiency gain from skipping JSON.
|
|
2. Testing JSON is much easier than testing erlang term format.
|
|
3. Erlang term format can give an illusion of extra security but unless the transport is HTTPS
|
|
then the communication is still inherently unsafe.
|
|
4. Erlang term format is difficult to handle for unfamiliar developers, you need to remember
|
|
about safe conversion to avoid atom exhaustion attacks or sending an `rm -rf /` function over
|
|
the wire.
|
|
|
|
The endpoint is protected by simple authentication that requires the secret key of all servers
|
|
being the same. It is good enough for a demo, but for any real application it would need to be
|
|
reconsidered. HTTPS would be a non-negotiable requirement for any user-facing deployment.
|
|
"""
|
|
|
|
use BirdyChatWeb, :controller
|
|
|
|
def create(conn, params) do
|
|
with true <- authorised?(conn.req_headers, params),
|
|
{:ok, changeset} <- BirdyChat.Message.validate_for_inter_server_use(params),
|
|
:ok <- BirdyChat.MessageWriter.write(changeset.changes) do
|
|
conn
|
|
|> put_status(:created)
|
|
|> render(:create, message: changeset.changes)
|
|
else
|
|
_any ->
|
|
conn
|
|
|> put_status(:forbidden)
|
|
|> render(:error, message: "Unauthorised")
|
|
end
|
|
end
|
|
|
|
defp authorised?(headers, %{"from" => from}) do
|
|
case Enum.find(headers, fn {key, _value} -> key == "authorization" end) do
|
|
nil ->
|
|
false
|
|
|
|
{"authorization", token} ->
|
|
case Phoenix.Token.verify(BirdyChatWeb.Endpoint, "serverAuth", token, max_age: 1200) do
|
|
{:ok, id} -> id == from
|
|
{:error, :invalid} -> false
|
|
end
|
|
end
|
|
end
|
|
end
|