Permit.Phoenix (permit_phoenix v0.4.0)
View SourcePhoenix, Plug and LiveView integrations integration for Permit.
In Phoenix controller actions as well as LiveView modules, based on the resource configured for the current
controller or LiveView (e.g. an Article), the current user, and the action (determined by the controller
action or :live_action), it typically performs the following operations:application
Based on current params, typically an ID parameter, preload a record which is the subject of the current action. This is done either from the DB when
Permit.Ectois used or in a different manner.Call to the application's authorization module (the one that has
use Permit- refer to docs forPermitfor examples) to ask whether the preloaded record is authorized to perform the current controller action.Expose the loaded record into
assignsif authorized, or perform a defined action (e.g. redirect) otherwise.
Setup
In your mix.exs, add :permit_phoenix to your dependencies. Also, add :permit_ecto unless you don't use
Ecto for managing relationships between users and data and sourcing attributes important for authorization.
def deps do
[
{:permit_phoenix, "~> 0.4.0"},
{:permit_ecto, "~> 0.2.4"}
]
endFor convenience defining permissions using action names corresponding to controller and live actions,
create an actions module that extends Permit.Phoenix.Actions and merges the actions from your router. This module
also declares which actions are considered singular (operating on a single resource) or plural (operating on a
list of resources).
After configuring action names, configure and define your permissions [(see Permit docs for details)]
(https://hexdocs.pm/permit/Permit.html#module-configure-define-your-permissions). With Permit.Ecto, use
Permit.Ecto.Permissions in your permissions module.
Once you've got a configured MyApp.Authorization module, use it in your controller and live view modules.
Controller usage
To setup a controller to use Permit.Phoenix, use Permit.Phoenix.Controller:
defmodule MyAppWeb.ArticleController do
use Permit.Phoenix.Controller,
authorization_module: MyApp.Authorization,
resource_module: MyApp.Article
def index(conn, params) do
# @loaded_resources is assigned if authorized, containing the list of records
render(conn, :index)
end
def show(conn, params) do
# @loaded_resource is assigned if authorized, containing the loaded record
render(conn, :show)
end
endBy default, in :show and :index, Permit will check for the :read permission on the MyApp.Article resource
by the current user (@current_scope.user). Permit.Ecto will generate a query filtered by permission conditions
and to fetch the record (or records) by the "id" param, by default.
If authorized, the record will be assigned to @loaded_resource. Otherwise, the handle_unauthorized/2 callback
will be called, which defaults to redirecting to /.
You can customize the authorization check by overriding the handle_unauthorized/2 callback.
@impl true
def handle_unauthorized(action, conn) do
redirect(conn, to: "/")
endYou can put the configuration in your main web module and override specific options in individual controllers.
defmodule MyAppWeb do
def controller do
quote do
use Permit.Phoenix.Controller,
authorization_module: MyApp.Authorization
end
end
end
defmodule MyAppWeb.ArticleController do
use MyAppWeb, :controller
@impl true
def resource_module, do: MyApp.Article
# Controller actions...
endSee Permit.Phoenix.Controller for more information on how to handle authorization errors, customize
generated queries, and other available options and callbacks.
That's it - the preload, the current user fetching, the assignment of loaded record and the handling of authorization errors is automatic.
LiveView usage
On mount, Permit.Phoenix.LiveView attaches hooks to the handle_params/3 and handle_event/3 callbacks to preload records
and perform authorization:
- at mount, hooked before the
mount/3callback - during navigation, when
:live_actionchanges - beforehandle_params/3is called - in event handling, when an event is triggered - before
handle_event/3is called
Action name is either taken from the :live_action defined for the current route, or - in event authorization - from an explicit
mapping between event names and Permit action names using the @permit_action module attribute.
Record ID is taken from the "id" param, by default - both in navigation and in event handling. In events that do not carry
an ID param, the record currently assigned to @loaded_resource is used and reloaded to ensure permissions are evaluated against the
latest data.
To setup a LiveView to use Permit.Phoenix, use Permit.Phoenix.LiveView:
defmodule MyApp.ArticleLive.Show do
use MyAppWeb, :live_view
use Permit.Phoenix.LiveView,
authorization_module: MyApp.Authorization,
resource_module: MyApp.Article
endJust like with controllers, you can put the configuration in your main web module and override specific options in individual LiveViews.
defmodule MyAppWeb do
def live_view do
quote do
use Permit.Phoenix.LiveView,
authorization_module: MyApp.Authorization
end
end
end
defmodule MyApp.ArticleLive.Show do
use MyAppWeb, :live_view
@impl true
def resource_module, do: MyApp.Article
endParam and event handlers put preloaded and authorized record in @loaded_resource or @loaded_resources assigns, respectively.
For plural actions like :index, the records can be streamed to @streams.loaded_resources instead, if :use_stream option
is set to true.
defmodule MyApp.ArticleLive.Index do
use MyAppWeb, :live_view
use Permit.Phoenix.LiveView,
authorization_module: MyApp.Authorization,
resource_module: MyApp.Article,
use_stream?: true
@impl true
def handle_params(params, _url, socket) do
# @streams.loaded_resources is assigned if authorized, containing the list of records
{:noreply, socket}
end
@impl true
@permit_action :delete
def handle_event("delete", params, socket) do
# @loaded_resource is assigned if authorized, containing the loaded record
# Delete the record and stream the deletion to the client
end
endIf authorization fails, handle_unauthorized/2 is called, which can either :cont or :halt the processing.
By default, it stays on the same page and displays a flash error message unless it's in the mounting phase or
cannot determine the live referer.
In events that do not carry an ID param (e.g. form updating with "save" and form data in the params), authorization
to perform the current action in general is still performed - the record currently assigned to @loaded_resource
is used and reloaded to ensure authorization correctness, unless reload_on_event?/2 is overridden to false.
See Permit.Phoenix.LiveView for more information on how to handle authorization errors, customize
generated queries, and other available options and callbacks.