Current section

Files

Jump to
couchdb_auth lib couchdb_auth tesla middleware couchdb_cookie_auth.ex
Raw

lib/couchdb_auth/tesla/middleware/couchdb_cookie_auth.ex

defmodule Tesla.Middleware.CouchDB.CookieAuth do
@moduledoc """
Middleware implementing CouchDB cookie (session) authentication.
The middleware performs a session request against `/_session` to obtain a
cookie and injects it into the outbound request.
"""
alias CouchDB.Environment
alias Tesla.Env
alias Tesla.Middleware
@behaviour Tesla.Middleware
@default_adapter {Tesla.Adapter.Hackney, []}
@impl Tesla.Middleware
def call(%Env{} = env, next, opts) do
opts = Map.new(opts || %{})
with {:ok, cookie} <- fetch_cookie(opts) do
env
|> ensure_absolute_url(opts[:url])
|> Tesla.put_header("cookie", cookie)
|> Tesla.run(next)
end
end
defp fetch_cookie(%{username: username, password: password} = opts)
when is_binary(username) and is_binary(password) do
base_url = Map.get(opts, :url) || Environment.base_url()
client =
Tesla.client(
[
{Middleware.BaseUrl, base_url},
{Middleware.Headers, [{"content-type", "application/json"}]},
{Middleware.JSON, engine: Jason}
],
@default_adapter
)
case Tesla.post(client, "/_session", %{name: username, password: password}) do
{:ok, %Env{status: status, headers: headers}} when status in 200..299 ->
case find_header(headers, "set-cookie") do
nil -> {:error, :missing_cookie}
cookie -> {:ok, cookie}
end
{:ok, %Env{} = response} ->
{:error, {:unexpected_status, response.status}}
{:error, reason} ->
{:error, reason}
end
end
defp fetch_cookie(_opts), do: {:error, :missing_credentials}
defp ensure_absolute_url(env, nil), do: env
defp ensure_absolute_url(%Env{url: url} = env, base_url) do
case URI.parse(url) do
%URI{scheme: nil} -> %{env | url: to_string(base_url) <> url}
%URI{scheme: ""} -> %{env | url: to_string(base_url) <> url}
_ -> env
end
end
defp find_header(headers, key) do
headers
|> List.keyfind(key, 0, nil)
|> case do
nil ->
headers
|> Enum.find(fn {k, _v} -> String.downcase(k) == String.downcase(key) end)
header ->
header
end
|> case do
nil -> nil
{_key, value} -> value
end
end
end