Packages
Plug based authorization and resource loading. Aimed at Phoenix, but depends only on Plug. MIT license Updated to compile clean with Elixir 1.4
Current section
Files
Jump to
Current section
Files
lib/load.ex
defmodule PolicyWonk.Load do
@moduledoc """
This turns your resource module into a plug that can be used in a router.
## Usage
The only time you should directly use the `PolicyWonk.Load` module is to call
`use PolicyWonk.Load` when defining your resource module.
Example resource loader module:
defmodule MyAppWeb.Resources do
use PolicyWonk.Resource # set up support for resources
use PolicyWonk.Load # turn this module into an resource loading into a plug
def resource( _conn, :user, %{"id" => user_id} ) do
case MyAppWeb.Account.get_user(user_id) do
nil -> {:error, :not_found}
user -> {:ok, :user, user}
end
end
def policy_error(conn, :not_found) do
MyAppWeb.ErrorHandlers.resource_not_found(conn, "Resource Not Found")
end
end
To use your resources as a plug, you can just use the new module you created.
Load resources in a router:
pipeline :browser_session do
plug MyAppWeb.Resources, :user
plug MyAppWeb.Resources, [:thing_a, :thing_b]
end
You can also pass in a Keyword list with additional options
plug MyAppWeb.Resources, resources: [:user, :thing_a], async: true
If you use the keyword list of options, then the available options are
* resources: a list of resources to load
* async: a true/false flag indicating if the resources should be loaded asynchronously
## async load default
When you add `use PolicyWonk.Load` to your resource module, the default is to set async
loading to false. If you would like to set it as true, you can set as a use option.
defmodule MyAppWeb.Resources do
use PolicyWonk.Resource # set up support for resources
use PolicyWonk.Load, async: true # turn this module into an resource loading into a plug
"""
# ===========================================================================
# the using macro for loaders adopting this behavioiur
defmacro __using__(use_opts) do
quote do
@doc false
def init(resources_or_opts) do
PolicyWonk.Load.plug_init(
resources_or_opts,
unquote(use_opts[:resource_module]) || __MODULE__,
unquote(use_opts[:async])
)
end
@doc false
def call(conn, opts), do: PolicyWonk.Load.call(conn, opts)
end
# quote
end
# defmacro
# ===========================================================================
# define a policy error here - not found or something like that
defmodule Error do
@moduledoc false
defexception message: "#{IO.ANSI.red()}Load resource failed#{IO.ANSI.default_color()}\n"
end
# ===========================================================================
# --------------------------------------------------------
@doc false
def init(opts) when is_list(opts) do
async =
case opts[:async] do
true -> true
_ -> false
end
do_init(opts[:resource_module], opts[:resources], async)
end
# --------------------------------------------------------
@doc false
def plug_init(resources_or_opts, module, async) do
case Keyword.keyword?(resources_or_opts) do
true ->
resources_or_opts
|> Keyword.put_new(:resource_module, module)
|> Keyword.put_new(:async, async)
|> init()
false ->
do_init(module, resources_or_opts, false)
end
end
# --------------------------------------------------------
defp do_init(nil, _, _),
do:
raise(
Error,
message: "#{IO.ANSI.red()}Must supply a valid :resource_module#{IO.ANSI.default_color()}"
)
defp do_init(_, [], _),
do:
raise(
Error,
message:
"#{IO.ANSI.red()}Must supply at least one resource to load#{IO.ANSI.default_color()}"
)
defp do_init(resource_module, resources, async)
when is_atom(resource_module) and is_list(resources) do
%{
resource_module: resource_module,
resources: resources,
async: async
}
end
defp do_init(policy_module, policy, async) do
do_init(policy_module, [policy], async)
end
# --------------------------------------------------------
@doc false
def call(conn, %{resource_module: resource_module, resources: resources, async: async}) do
PolicyWonk.Resource.load(conn, resource_module, resources, async)
end
end