Permit.Phoenix.Actions (permit_phoenix v0.4.0)

View Source

Defines action names for your permissions schema, so that convenience functions are generated for them.

defmodule MyApp.Actions do
  # Merge the actions from the router into the default grouping schema
  use Permit.Phoenix.Actions, router: MyApp.Router

  # Additional singular actions throughout the app
  def singular_actions, do: [:view]
end

Overview

Default action grouping for Phoenix includes:

  • :create, implying permission to :new and :create actions
  • :read, implying permission to :index and :show actions
  • :update, implying permission to :edit and :update actions
  • :delete, implying permission to :delete action

By granting a permission to :read, you also allow :index and :show. You can also grant permissions to individual actions specifically - if you only grant :index, :show will not be authorized.

This is coded as:

%{
  create: [],
  read: [],
  update: [],
  delete: [],
  new: [:create],
  index: [:read],
  show: [:read],
  edit: [:update]
}

If you want to declare an action that requires more than one permission, you can define a grouping:

%{
  access: [],
  view: [],
  open: [:accsess, :view] # if both are granted, :open will be authorized
}

Singular vs plural actions

Singular actions are those that operate on a single resource, such as :show or :update. Plural actions operate on a collection of resources, such as :index. Depending on the arity, queries generated by Permit.Ecto will be executed as LIMIT 1 (for singular actions) or with any limit given in the base_query (for plural actions).

Each action in Permit is assumed to be plural by default. To declare an action as singular, you can implement the singular_actions/0 callback. This can be implemented in your actions module and overridden in your controller or LiveView module.

Default singular actions are :show, :edit, :new, :delete, :update and those inferred from the router - see section below. The singular_actions/0 callback can be used to add more

  • it does not require calling super.

Example

defmodule MyApp.Actions do
  use Permit.Phoenix.Actions

  # In addition to the default singular actions. If `:view` always deals with a single record
  # throughout the app, it can be declared as singular in this module.

  @impl true
  def singular_actions, do: [:view]
end

Actions can also be configured as singular or plural in the controller or LiveView module itself, which takes precedence over the actions module.

If a controller or LiveView contains a :view action that deals with an index of records, it can be overridden as plural in the controller or LiveView module. super() will contain the actions module's configuration.

defmodule MyApp.ArticleController do
  use Permit.Phoenix.Controller

  @impl true
  def singular_actions, do: super() -- [:view]
end

Router

It is recommended to read action names from the router, so that all controller action and :live_action names are automatically included and convenience functions for them are generated. Moreover, actions are automatically inferred to be singular or plural based on the route definition. An action is singular by default if:

  • it's one of: :show, :edit, :new, :delete, :update, :create, or
  • it is a POST request, or
  • it's a route with an :id, :uuid or :slug parameter, e.g. /items/:id/view or /items/:uuid/view, or
  • the route's last segment is a parameter, e.g. /items/:name, /items/:identifier.
defmodule MyApp.Router do
  # ...

  get("/items/:id", MyApp.ItemController, :view)
end

defmodule MyApp.Actions do
  # Merge the actions from the router into the default grouping schema.
  use Permit.Phoenix.Actions, router: MyApp.Router

  # This doesn't need to be used - Permit automatically infers that the :view action is
  # singular.
  # You can use it if an explicit declaration is needed, e.g. if you use a different ID
  # parameter than `:id`, `:uuid` or `:slug`.
  @impl true
  def singular_actions, do: [:view]
end

defmodule MyApp.Permissions do
  # Use the actions module to define permissions.
  use Permit.Permissions, actions_module: MyApp.Actions

  def can(%User{role: :admin} = _user) do
    permit()
    |> all(Item)
  end

  # The `view` action is automatically added to the grouping schema
  # and hence available as a `view/2`function when defining permissions.
  def can(%User{role: :owner} = _user) do
    permit()
    |> view(Item)
    |> all(Item, fn user, item -> item.owner_id == user.id end)
  end
end

Summary

Functions

Convenience function defining the basic CRUD (create, read, update, delete) actions.

Convenience function returning actions that are singular in the most basic CRUD setup, in which case all of: :create, :read, :update and :delete are singular.

Returns the default action grouping schema for Phoenix applications.

Returns the list of actions that operate on a single resource.

Functions

action_names_from_router(router_module)

crud_grouping()

Convenience function defining the basic CRUD (create, read, update, delete) actions.

crud_singular()

Convenience function returning actions that are singular in the most basic CRUD setup, in which case all of: :create, :read, :update and :delete are singular.

filtered_routes_stream(router_module)

grouping_schema()

Returns the default action grouping schema for Phoenix applications.

merge_from_router(grouping_schema, router_module)

paths_from_router(router_module)

singular_actions(router_module)

Returns the list of actions that operate on a single resource.