Current section

Files

Jump to
couchdb_auth lib couchdb_auth.ex
Raw

lib/couchdb_auth.ex

defmodule CouchDB do
@moduledoc """
Lightweight CouchDB client builder focused on authentication flows.
The module exposes helpers to build a pre-configured `Tesla.Client`
alongside a thin wrapper around `Tesla.request/2` to keep the original
API surface available.
"""
alias CouchDB.Environment
alias Tesla.Middleware
@type interface :: :base | :backend
@default_headers [{"user-agent", "couchdb_auth"}]
@default_adapter {Tesla.Adapter.Hackney, []}
@doc """
Build a `Tesla.Client` configured for the requested interface.
The `interface` can be `:base` (default) or `:backend`. Additional
credential overrides may be supplied via `overrides`.
"""
@spec client(interface(), map()) :: Tesla.Client.t()
def client(interface \\ :base, overrides \\ %{}) do
url = interface_url(interface)
overrides = Map.new(overrides)
admin = Environment.server_admin()
credentials =
%{
username: Map.get(overrides, :username, admin.user),
password: Map.get(overrides, :password, admin.password),
url: url
}
|> Map.merge(Map.drop(overrides, [:username, :password]))
middleware =
[
{Middleware.BaseUrl, url},
{Middleware.Headers, @default_headers},
{Middleware.JSON, engine: Jason}
] ++ auth_middleware(Environment.auth_type(), credentials, url)
Tesla.client(middleware, @default_adapter)
end
@doc """
Executes an HTTP request using either the provided `%Tesla.Client{}` or a
freshly built one.
"""
@spec request(atom(), String.t(), keyword()) :: {:ok, Tesla.Env.t()} | {:error, any()}
def request(method, url, opts \\ []) do
{client, opts} = pop_client(opts)
request_opts =
opts
|> Keyword.put(:method, method)
|> Keyword.put(:url, url)
Tesla.request(client, request_opts)
end
for verb <- ~w(delete get head options patch post put trace)a do
@spec unquote(verb)(String.t(), keyword()) :: {:ok, Tesla.Env.t()} | {:error, any()}
def unquote(verb)(url, opts \\ []) do
request(unquote(verb), url, opts)
end
end
defp pop_client(opts) do
case Keyword.pop(opts, :client) do
{nil, rest_opts} ->
{interface, rest_opts} = Keyword.pop(rest_opts, :interface, :base)
overrides = rest_opts |> Keyword.get(:credentials, %{}) |> Map.new()
rest_opts = Keyword.drop(rest_opts, [:credentials])
{client(interface, overrides), rest_opts}
{client, rest_opts} ->
{client, rest_opts}
end
end
defp interface_url(:backend), do: Environment.backend_url()
defp interface_url(_), do: Environment.base_url()
defp auth_middleware(:admin_party, _opts, _url), do: []
defp auth_middleware(:basic, opts, _url) do
[{Tesla.Middleware.CouchDB.BasicAuth, opts}]
end
defp auth_middleware(:cookie, opts, url) do
[{Tesla.Middleware.CouchDB.CookieAuth, Map.put(opts, :url, url)}]
end
defp auth_middleware(:oauth, opts, url) do
credentials =
Environment.oauth_creds()
|> Map.merge(%{url: url})
|> Map.merge(opts)
[{Tesla.Middleware.CouchDB.OAuth, credentials}]
end
defp auth_middleware(_, _opts, _url), do: []
end