Permit.Phoenix.Plug (permit_phoenix v0.3.0)
View SourceAuthorization plug for the web application.
It automatically infers what CRUD action is represented by the currently executed controller action, and delegates to Permit to determine whether the action is authorized based on current user's role.
Current user is always fetched from conn.assigns[:current_user]
, and their role is taken
from the module specified in the app's use Permit
directive. For instance if it's
configured as follows:
# Authorization configuration module
defmodule MyApp.Authorization do
use Permit,
repo: Lvauth.Repo,
permissions_module: MyApp.Authorization.Permissions
end
# Permissions module - just as an example
defmodule MyApp.Authorization.Permissions do
use Permit.Permissions
def can(%{role: :manager} = role) do
# A :manager can do all CRUD actions on RouteTemplate, and can do :read on User
# if the User has public: true OR the User has :overseer_id equal to current user's
# id.
permit()
|> all(Lvauth.Planning.RouteTemplate)
|> read(Lvauth.Accounts.User, public: true)
|> read(LvMainFrame.Accounts.User,
fn user, other_user -> other_user.overseer_id == user.id end)
end
end
Then controller can be configured the following way:
defmodule LvauthWeb.Planning.RouteTemplateController do
plug Permit.Phoenix.Plug,
authorization_module: MyApp.Authorization,
loader: fn id -> Lvauth.Repo.get(Customer, id) end,
resource_module: Lvauth.Management.Customer,
id_param_name: "id",
except: [:example],
fetch_subject: fn conn -> conn.assigns[:signed_in_user] end
handle_unauthorized: fn conn -> redirect(conn, to: "/foo") end
def show(conn, params) do
# 1. If assigns[:current_user] is present, the "id" param will be used to call
# Repo.get(Customer, params["id"]).
# 2. can(role) |> read?(record) will be called on the loaded record and each user role.
# 3. If authorization succeeds, the record will be stored in assigns[:loaded_resources].
# 4. If any of the steps described above fails, the pipeline will be halted.
end
def index(conn, params) do
# 1. If assigns[:current_user] is present, can(role) |> read?(Customer) will be called on each role
# 2. If authorization succeeds, nothing happens.
# 3. If any of the steps described above fails, the pipeline will be halted.
end
def details(conn, params) do
# Behaves identically to :show because in :action_crud_mapping it's defined as :read.
end
def clone(conn, params) do
# 1. If assigns[:current_user] is present, the "id" param will be used to call
# Repo.get(Customer, params["id"]).
# 2. can(role) |> update?(record) will be called on the loaded record (as configured in :action_crud_mapping) and each role of user
# 3. If authorization succeeds, the record will be stored in assigns[:loaded_resources].
# 4. If any of the steps described above fails, the pipeline will be halted.
end
end
##
Summary
Functions
@spec call(Permit.Phoenix.Types.conn(), Permit.Phoenix.Types.plug_opts()) :: Plug.Conn.t()
@spec init(Permit.Phoenix.Types.plug_opts()) :: Permit.Phoenix.Types.plug_opts()