Current section
Files
Jump to
Current section
Files
lib/plug/authorize.ex
defmodule KeenAuth.Plug.Authorize do
require Logger
@default_operation :and
@default_handler KeenAuth.Plug.AuthorizationErrorHandler
def groups(conn, opts) when is_map(opts) do
check(conn, opts, :groups)
end
def groups(conn, opts) when is_list(opts) do
config = build_config(opts)
check(conn, config, :groups)
end
def roles(conn, opts) when is_map(opts) do
check(conn, opts, :roles)
end
def roles(conn, opts) when is_list(opts) do
config = build_config(opts)
check(conn, config, :roles)
end
def permissions(conn, opts) when is_map(opts) do
check(conn, opts, :permissions)
end
def permissions(conn, opts) when is_list(opts) do
config = build_config(opts)
check(conn, config, :permissions)
end
def build_config(opts) do
%{
actions: Keyword.get(opts, :only) |> allowed_actions(),
required: Keyword.fetch!(opts, :required) |> Enum.map(&normalize_value/1),
operation: Keyword.get(opts, :op, @default_operation),
handler: Keyword.get(opts, :error_handler, @default_handler)
}
end
defp check(conn, config, key) do
if is_nil(config.actions) or conn.private.phoenix_action in config.actions do
current_user = KeenAuth.current_user(conn)
allowed = is_allowed(current_user, config, key)
resolve_authorization(conn, allowed, config.handler)
else
conn
end
end
# Unauthenticated user not allowed
defp is_allowed(nil, _, _), do: false
defp is_allowed(current_user, config, key) do
current_user
|> fetch_user_values(key)
|> Enum.map(&normalize_value/1)
|> check_user_values(config.required, config.operation)
end
defp check_user_values(_, nil, _), do: false
defp check_user_values(user_values, required_values, :or) do
user_values
|> has_any_value(required_values)
end
defp check_user_values(user_values, required_values, :and) do
user_values
|> has_all_values(required_values)
end
defp fetch_user_values(user, :permissions), do: user.permissions
defp fetch_user_values(user, :roles), do: user.roles
defp fetch_user_values(user, :groups), do: user.groups
defp allowed_actions(nil), do: nil
defp allowed_actions(action) when is_atom(action), do: [action]
defp allowed_actions(actions) when is_list(actions), do: actions
# Forbidden handler
defp resolve_authorization(conn, false, handler) do
conn
|> handler.call(:unauthorized)
|> Plug.Conn.halt()
end
defp resolve_authorization(conn, true, _), do: conn
defp normalize_value(value) when is_atom(value) do
value
|> Atom.to_string()
|> normalize_value()
end
defp normalize_value(value) when is_binary(value) do
value
|> String.downcase()
end
defp has_all_values(user_values, required_values) do
Enum.all?(required_values || [], &(&1 in (user_values || [])))
end
defp has_any_value(user_values, required_values) do
Enum.any?(required_values || [], &Enum.member?(user_values || [], &1))
end
end