Permit.Phoenix.LiveView behaviour (permit_phoenix v0.3.0)
View SourceA live view module using the authorization mechanism should mix in the LiveViewAuthorization module:
defmodule MyAppWeb.DocumentLive.Index
use Permit.Phoenix.LiveView
@impl true
def resource_module, do: Document
# Override default action groupings if needed
@impl true
def action_grouping do
%{
new: [:create],
index: [:read],
show: [:read],
edit: [:update],
create: [:create],
update: [:update],
delete: [:delete]
}
end
# Override singular actions if needed
@impl true
def singular_actions do
[:show, :edit, :new, :delete, :update]
end
end
which adds the LiveViewAuthorization behavior with the following callbacks to be implemented - for example:
# The related schema
@impl true
def resource_module, do: Document
# Loader function for a singular resource in appropriate actions (:show, etc.); usually a context
# function. If not defined, Repo.get is used by default.
@impl true
def loader, do: fn id -> get_organization!(id) end
# How to fetch the current user from session - for instance:
@impl true
def fetch_subject(socket, session) do
with token when not is_nil(token) <- session["token"],
%User{} = current_user <- get_user(token) do
current_user
else
_ -> nil
end
end
Optionally, p handle_unauthorized/2 optional callback can be implemented, returning {:cont, socket} or {:halt, socket}. The default implementation returns:
{:halt, socket(socket, to: socket.view.fallback_path())}
Summary
Callbacks
Defines the action grouping schema for this live view. This can be overridden in individual live views to customize the action mapping.
Defines which actions are considered singular (operating on a single resource). This can be overridden in individual live views to customize the singular actions.
Functions
Returns true if inside mount/1, false otherwise. Useful for distinguishing between rendering directly via router or being in a handle_params lifecycle.
Callbacks
@callback action_grouping() :: map()
Defines the action grouping schema for this live view. This can be overridden in individual live views to customize the action mapping.
@callback authorization_module() :: Permit.Types.authorization_module()
@callback base_query(Permit.Types.resolution_context()) :: Ecto.Query.t()
@callback event_mapping() :: map()
@callback except() :: [Permit.Types.action_group()]
@callback fallback_path(Permit.Types.action_group(), Permit.Phoenix.Types.socket()) :: binary()
@callback fetch_subject(Permit.Phoenix.Types.socket(), map()) :: Permit.Types.subject()
@callback finalize_query(Ecto.Query.t(), Permit.Types.resolution_context()) :: Ecto.Query.t()
@callback handle_not_found(Permit.Phoenix.Types.socket()) :: Permit.Phoenix.Types.hook_outcome()
@callback handle_unauthorized(Permit.Types.action_group(), Permit.Phoenix.Types.socket()) :: Permit.Phoenix.Types.hook_outcome()
@callback id_param_name(Permit.Types.action_group(), Permit.Phoenix.Types.socket()) :: binary()
@callback id_struct_field_name(Permit.Types.action_group(), Permit.Phoenix.Types.socket()) :: atom()
@callback loader(Permit.Types.resolution_context()) :: Permit.Types.object() | nil
@callback preload_actions() :: [Permit.Types.action_group()]
@callback resource_module() :: module()
@callback singular_actions() :: [atom()]
Defines which actions are considered singular (operating on a single resource). This can be overridden in individual live views to customize the singular actions.
@callback unauthorized_message(Permit.Phoenix.Types.socket(), map()) :: binary()
@callback use_stream?(Permit.Phoenix.Types.socket()) :: boolean()
Functions
@spec mounting?(Permit.Phoenix.Types.socket()) :: boolean()
Returns true if inside mount/1, false otherwise. Useful for distinguishing between rendering directly via router or being in a handle_params lifecycle.
For example, a handle_unauthorized/1 implementation must redirect when halting during mounting, while it needn't redirect when halting during the handle_params lifecycle.
Example
@impl true
def handle_unauthorized(socket) do
if mounting?(socket) do
{:halt, push_redirect(socket, to: "/foo")}
else
{:halt, assign(socket, :unauthorized, true)}
end
end