Current section

Files

Jump to
authorize lib authorize.ex
Raw

lib/authorize.ex

defmodule Authorize.Inline do
defmacro __using__(_options) do
quote do
use Authorize
import unquote(__MODULE__)
end
end
defmacro authorize(do: block) do
quote do
unquote(block)
end
end
end
defmodule Authorize do
defmacro __using__(_options) do
quote do
import unquote(__MODULE__)
Module.register_attribute(__MODULE__, :rules, accumulate: true)
@before_compile unquote(__MODULE__)
end
end
def create_authorize() do
quote do
def is_changeset?(%{__struct__: :"Elixir.Ecto.Changeset"}), do: true
def is_changeset?(_), do: false
def get_struct(%{__struct__: :"Elixir.Ecto.Changeset"} = changeset), do: changeset.data
def get_struct(struct), do: struct
def authorize_fields(struct_or_changeset, actor, actions, field, options \\ [])
def authorize_fields(struct_or_changeset, actor, actions, field, options) when is_atom(field) do
authorize(struct_or_changeset, actor, actions, Keyword.put(options, :fields, [field]))
end
def authorize_fields(struct_or_changeset, actor, actions, fields, options) do
authorize(struct_or_changeset, actor, actions, Keyword.put(options, :fields, fields))
end
def authorize(struct_or_changeset, actor, action, options \\ []) do
@rules
|> Enum.reverse
# filter based on actions
|> Enum.filter(fn
{acts, _, _} when is_atom(acts) -> acts == :all || acts == action
{acts, _, _} -> Enum.member?(acts, :all) || Enum.member?(acts, action)
end)
|> Enum.reduce(:undecided, fn
({_actions, description, rule_func}, :undecided) ->
apply(__MODULE__, rule_func, [struct_or_changeset, Keyword.get(options, :fields, []), actor])
(_fun, other) -> other
end)
|> case do
:undecided ->
{:unauthorized, struct_or_changeset, "no authorization rule found"}
:ok ->
{:ok, struct_or_changeset}
{:ok, reason} ->
if Keyword.get(options, :include_reason, false) do
{:ok, struct_or_changeset, reason}
else
{:ok, struct_or_changeset}
end
{:unauthorized, reason} ->
{:unauthorized, struct_or_changeset, reason}
other -> other
end
end
def authorize(struct_or_changeset, actor), do: authorize(struct_or_changeset, actor, :all)
end
end
defmacro __before_compile__(_env) do
create_authorize()
end
# def apply_rule(description, struct_or_changeset, actor) do
# apply(__MODULE__, String.to_atom(description), [struct_or_changeset, actor])
# end
def create_rule(actions, description, struct_or_changeset, fields, actor, do: rule_block) do
rule_func = String.to_atom(description)
quote do
@rules {unquote(actions), unquote(description), unquote(rule_func)}
def unquote(rule_func)(unquote(struct_or_changeset), unquote(fields), unquote(actor)) do
case unquote(rule_block) do
:undecided -> :undecided
:ok -> {:ok, unquote(description)}
:unauthorized -> {:unauthorized, unquote(description)}
# make composition of authorization functions possible
{:ok, _struct_or_changeset} -> :ok
{:ok, _struct_or_changeset, description} -> {:ok, description}
{:unauthorized, _struct_or_changeset, "no authorization rule found"} -> :undecided
{:unauthorized, _struct_or_changeset, description} -> {:unauthorized, description}
end
end
end
end
defmacro rule(description, struct_or_changeset, actor, do: rule_block) when is_binary(description) do
fields = []
create_rule(:all, description, struct_or_changeset, quote(do: fields), actor, do: rule_block)
end
defmacro rule(description, changeset, fields, actor, do: rule_block) when is_binary(description) do
create_rule(:all, description, changeset, fields, actor, do: rule_block)
end
defmacro rule(actions, description, struct_or_changeset, actor, do: rule_block) when is_binary(description) do
fields = []
create_rule(actions, description, struct_or_changeset, quote(do: fields), actor, do: rule_block)
end
defmacro rule(actions, description, struct_or_changeset, fields, actor, do: rule_block) when is_binary(description) do
create_rule(actions, description, struct_or_changeset, fields, actor, do: rule_block)
end
end