diff --git a/lib/ja_resource/authorize.ex b/lib/ja_resource/authorize.ex new file mode 100644 index 0000000..b29ea78 --- /dev/null +++ b/lib/ja_resource/authorize.ex @@ -0,0 +1,24 @@ +defmodule JaResource.Authorize do + use Behaviour + + @moduledoc """ + Provides the `handle_authorize/0` callback used to authorize the resource. + + This behaviour is used by all JaResource actions. + """ + + @doc """ + Called before all the actions with the model. Useful for authorizing. + """ + @callback handle_authorize(Plug.Conn.t, JaResource.record) :: any + + defmacro __using__(_) do + quote do + @behaviour JaResource.Authorize + + def handle_authorize(model, _conn), do: model + + defoverridable [handle_authorize: 2] + end + end +end diff --git a/lib/ja_resource/create.ex b/lib/ja_resource/create.ex index 77d2de2..73df86f 100644 --- a/lib/ja_resource/create.ex +++ b/lib/ja_resource/create.ex @@ -65,6 +65,9 @@ defmodule JaResource.Create do def call(controller, conn) do merged = JaResource.Attributes.from_params(conn.params) attributes = controller.permitted_attributes(conn, merged, :create) + + controller.handle_authorize(controller.model(), conn) + conn |> controller.handle_create(attributes) |> JaResource.Create.insert(controller) @@ -84,7 +87,9 @@ defmodule JaResource.Create do @doc false def respond(%Plug.Conn{} = conn, _old_conn), do: conn + def respond({:error, _name, errors, _changes}, conn), do: invalid(conn, errors) def respond({:error, errors}, conn), do: invalid(conn, errors) + def respond({:ok, %{} = map}, conn), do: created(conn, Map.fetch(map, controller.atom())) def respond({:ok, model}, conn), do: created(conn, model) def respond(model, conn), do: created(conn, model) diff --git a/lib/ja_resource/delete.ex b/lib/ja_resource/delete.ex index 59e852e..2ec7bd8 100644 --- a/lib/ja_resource/delete.ex +++ b/lib/ja_resource/delete.ex @@ -59,6 +59,8 @@ defmodule JaResource.Delete do def call(controller, conn) do model = controller.record(conn, conn.params["id"]) + controller.handle_authorize(model, conn) + conn |> controller.handle_delete(model) |> JaResource.Delete.respond(conn) @@ -67,7 +69,9 @@ defmodule JaResource.Delete do @doc false def respond(nil, conn), do: not_found(conn) def respond(%Plug.Conn{} = conn, _old_conn), do: conn + def respond({:ok, %{} = map}, conn), do: created(conn, Map.fetch(map, controller.atom())) def respond({:ok, _model}, conn), do: deleted(conn) + def respond({:error, _name, errors, _changes}, conn), do: invalid(conn, errors) def respond({:errors, errors}, conn), do: invalid(conn, errors) def respond(_model, conn), do: deleted(conn) diff --git a/lib/ja_resource/index.ex b/lib/ja_resource/index.ex index 89bfaa6..e8161da 100644 --- a/lib/ja_resource/index.ex +++ b/lib/ja_resource/index.ex @@ -119,6 +119,8 @@ defmodule JaResource.Index do Execute the index action on a given module implementing Index behaviour and conn. """ def call(controller, conn) do + controller.handle_authorize(controller.model(), conn) + conn |> controller.handle_index(conn.params) |> JaResource.Index.filter(conn, controller) diff --git a/lib/ja_resource/model.ex b/lib/ja_resource/model.ex index 0db75c5..5484258 100644 --- a/lib/ja_resource/model.ex +++ b/lib/ja_resource/model.ex @@ -25,10 +25,20 @@ defmodule JaResource.Model do defmacro __using__(_) do quote do @behaviour JaResource.Model + use JaResource.Authorize @inferred_model JaResource.Model.model_from_controller(__MODULE__) def model(), do: @inferred_model + def atom() do + model() + |> Atom.to_string + |> String.split(".") + |> List.last + |> String.downcase + |> String.to_atom + end + defoverridable [model: 0] end end diff --git a/lib/ja_resource/show.ex b/lib/ja_resource/show.ex index e37e95f..c03b18c 100644 --- a/lib/ja_resource/show.ex +++ b/lib/ja_resource/show.ex @@ -57,9 +57,11 @@ defmodule JaResource.Show do Execute the show action on a given module implementing Show behaviour and conn. """ def call(controller, conn) do - conn - |> controller.handle_show(conn.params["id"]) - |> JaResource.Show.respond(conn, controller) + model = controller.handle_show(conn, conn.params["id"]) + + controller.handle_authorize(model, conn) + + JaResource.Show.respond(model, conn, controller) end @doc false diff --git a/lib/ja_resource/update.ex b/lib/ja_resource/update.ex index 9d8f29a..5917dcf 100644 --- a/lib/ja_resource/update.ex +++ b/lib/ja_resource/update.ex @@ -72,6 +72,8 @@ defmodule JaResource.Update do merged = JaResource.Attributes.from_params(conn.params) attributes = controller.permitted_attributes(conn, merged, :update) + controller.handle_authorize(model, conn) + conn |> controller.handle_update(model, attributes) |> JaResource.Update.update(controller) @@ -92,7 +94,9 @@ defmodule JaResource.Update do @doc false def respond(%Plug.Conn{} = conn, _oldconn), do: conn def respond(nil, conn), do: send_resp(conn, :not_found, "") + def respond({:error, _name, errors, _changes}, conn), do: invalid(conn, errors) def respond({:error, errors}, conn), do: invalid(conn, errors) + def respond({:ok, %{} = map}, conn), do: created(conn, Map.fetch(map, controller.atom())) def respond({:ok, model}, conn), do: updated(conn, model) def respond(model, conn), do: updated(conn, model)