View Source Permit.Phoenix.Plug (permit_phoenix v0.2.0)

Authorization 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

##

Link to this section Summary

Link to this section Functions