Current section
Files
Jump to
Current section
Files
lib/authy_controller.ex
defmodule Authy.Controller do
@moduledoc """
Import this in your Phoenix or Plug application controller to gain convenience macros
for doing authorization.
"""
@doc """
Authorizes the controller action for the current user and executes the given block if successful.
def index(conn, _params) do
authorize Post do
# ...
end
end
def show(conn, %{id: id}) do
post = Repo.get(Post, id)
authorize post do
# ...
end
end
"""
defmacro authorize(term, opts \\ [], [do: block]) do
quote do
case Authy.Controller.Helpers.conn_authorization(var!(conn), unquote(term), unquote(opts)) do
:authorized -> unquote(block)
:unauthorized -> Authy.Controller.Helpers.unauthorized!(var!(conn))
:not_found -> Authy.Controller.Helpers.not_found!(var!(conn))
end
end
end
@doc """
Scopes the current resource based on the action and user.
def index(conn, _params) do
authorize Post do
posts = scope(Post) |> Repo.all
# ...
end
end
def show(conn, %{id: id}) do
post = scope(Post) |> Repo.get(id)
authorize post do
# ...
end
end
"""
defmacro scope(term, opts \\ []) do
quote do
Authy.Controller.Helpers.conn_scope(var!(conn), unquote(term), unquote(opts))
end
end
end
defmodule Authy.Controller.Helpers do
@moduledoc """
These are behind-the-scenes methods for handling controller authorization. These
are only exported because the macros need to call them from other modules,
and we don't want to clutter up controller modules with helper functions
when Authy.Controller is imported. You probably don't need to call
these functions directly.
"""
@doc """
Returns an atom specifying the authorization status for the current
controller action. Typically this is not called directly by the developer,
only indirectly through the Authy.Controller.authorize/2 macro.
Available options:
* nils: :unauthorized (default) or :not_found - action to take when resource is nil
* action: atom - override the controller action picked up from conn
* user: term - override the current user picked up from conn
* policy: atom - override the policy determined from the term
"""
def conn_authorization(conn, term, opts) do
# Figure out which function to call in the event of a nil term -
# :unauthorized (default) or :not_found
nil_function = opts[:nils] || Application.get_env(:authy, :nils, :unauthorized)
action = opts[:action] || get_action(conn)
user = opts[:user] || get_current_user(conn)
cond do
is_nil(term) -> nil_function
Authy.authorized?(user, action, term, opts) -> :authorized
true -> :unauthorized
end
end
@doc """
Returns a resource scope for a given controller action. Typically this is not
called directly by the developer, only indirectly through the
Authy.Controller.scope/2 macro.
Available options:
* action: atom - override the controller action picked up from conn
* user: term - override the current user picked up from conn
* policy: atom - override the policy determined from the term
"""
def conn_scope(conn, term, opts \\ []) do
action = opts[:action] || get_action(conn)
user = opts[:user] || get_current_user(conn)
Authy.scoped(user, action, term, opts)
end
@doc """
Call the "unauthorized" handler on the configured module. Raises a RuntimeError
if that handler is not configured.
"""
def unauthorized!(conn) do
case Application.get_env(:authy, :unauthorized_handler) do
nil
-> raise "You must configure an unauthorized_handler for Authy in your config.exs,
like this: config :authy, unauthorized_handler: {MyHandlerModule, :handle_unauthorized}"
{module, function}
-> apply(module, function, [conn])
end
end
@doc """
Call the "not found" handler on the configured module. Raises a RuntimeError
if that handler is not configured.
"""
def not_found!(conn) do
case Application.get_env(:authy, :not_found_handler) do
nil
-> raise "You must configure an not_found_handler for Authy in your config.exs,
like this: config :authy, not_found_handler: {MyHandlerModule, :handle_not_found}"
{module, function}
-> apply(module, function, [conn])
end
end
defp get_current_user(conn) do
conn.assigns[Application.get_env(:authy, :current_user, :current_user)]
end
defp get_action(conn) do
conn.assigns[:action] || conn.private[:phoenix_action]
end
end