Canary.Hooks (canary v2.0.0-dev)

Hooks functions for loading and authorizing resources for the LiveView events. If you want to authorize handle_params and handle_event LiveView callbacks you can use mount_canary macro to attach the hooks.

You can think about the mount_canary as something similar to plug but for LiveView events.

For handle_params it uses socket.assigns.live_action as :action. For handle_event it uses the event name as :action.

Note that the event_name is a string - but in Canary it's converted to an atom for consistency.

Canary.Hooks and Canary.Plugs are separate modules but they share the same API. You can define plugs for standard pages and hooks for LiveView events with the same opts.

For the authorization actions, when the :required is false (by default it's true) it might be nil. Then the Canada.Can implementation should be the module name of the model rather than a struct.

Example

  use Canary.Hooks

  mount_canary :load_and_authorize_resource,
    on: [:handle_params, :handle_event],
    model: Post,
    only: [:show, :edit, :update]

  mount_canary :authorize_resource,
    on: [:handle_event],
    model: Post,
    only: [:my_event],
    required: false

  # ...

  def handle_params(params, _uri, socket) do
    # resource is already loaded and authorized
    post = socket.assigns.post
  end

  def handle_event("my_event", _unsigned_params, socket) do
    # Only admin is allowed to perform my_event
  end

lib/abilities/user.ex:

  defimpl Canada.Can, for: User do

    def can?(%User{} = user, :my_event, Post), do: user.role == "admin"

    def can?(%User{id: id}, _, %Post{user_id: user_id}), do: id == user_id
  end

Summary

Functions

Authorize the :current_user for the ginve resource. If the :current_user is not authorized it will halt the socket.

Loads and autorize resource and assigns it to the socket. When resource is required it will halt the socket if the resource is not found. If the user is not authorized it will halt the socket.

Loads the resource and assigns it to the socket. When resource is required it will halt the socket if the resource is not found.

Mount canary authorization hooks on the current module. It creates a wrapper function to handle_params and handle_event, and attaches the hooks to the Live View.

Functions

authorize_resource(atom, event_name, arg3, socket, opts)

(since 2.0.0)

Authorize the :current_user for the ginve resource. If the :current_user is not authorized it will halt the socket.

For the authorization check, it uses the can?/3 function from the Canada.Can module -

can?(subject, action, resource) where:

  1. The subject is the :current_user from the socket assigns. The :current_user key can be changed in the opts or in the Application.get_env(:canary, :current_user, :current_user). By default it's :current_user.
  2. The action for handle_params is socket.assigns.live_action, for handle_event it uses the event name.
  3. The resource is the loaded resource from the socket assigns or the model name if the resource is not loaded and not required.

Required opts:

  • :model - Specifies the module name of the model to load resources from
  • :on - Specifies the LiveView lifecycle stages to attach the hook. Default :handle_params

Optional opts:

  • :only - Specifies which actions to authorize

  • :except - Specifies which actions for which to skip authorization

    For handle_params it uses socket.assigns.live_action as :action. For handle_event it uses the event name as :action.

  • :as - Specifies the resource_name to get from assigns

  • :current_user - Specifies the key in the socket assigns to get the current user

  • :required - Specifies if the resource is required, when it's not assigned in socket it will halt the socket

  • :unauthorized_handler - Specify a handler function to be called if the action is unauthorized

Example:


mount_canary :authorize_resource,
  model: Post,
  only: [:show, :edit, :update]
  current_user: :current_user

mount_canary :authorize_resource,
  model: Post,
  as: :custom_resource_name,
  except: [:new, :create],
  unauthorized_handler: {ErrorHandler, :unauthorized_handler}

load_and_authorize_resource(atom, params, unsigned_params, socket, opts)

(since 2.0.0)

Loads and autorize resource and assigns it to the socket. When resource is required it will halt the socket if the resource is not found. If the user is not authorized it will halt the socket.

It combines load_resource and authorize_resource functions.

Required opts:

  • :model - Specifies the module name of the model to load resources from
  • :on - Specifies the LiveView lifecycle stages to attach the hook. Default :handle_params

Optional opts:

  • :only - Specifies which actions to authorize

  • :except - Specifies which actions for which to skip authorization

    For handle_params it uses socket.assigns.live_action as :action. For handle_event it uses the event name as :action.

  • :as - Specifies the resource_name to use in assigns

  • :current_user - Specifies the key in the socket assigns to get the current user

  • :preload - Specifies association(s) to preload

  • :id_name - Specifies the name of the id in params, defaults to "id"

  • :id_field - Specifies the name of the ID field in the database for searching :id_name value, defaults to "id".

  • :required - Specifies if the resource is required, when it's not found it will halt the socket, default true

  • :not_found_handler - Specify a handler function to be called if the resource is not found

  • :unauthorized_handler - Specify a handler function to be called if the action is unauthorized

Example:


mount_canary :load_and_authorize_resource,
  model: Comments,
  id_name: :post_id,
  id_field: :post_id,
  only: [:comments]

mount_canary :load_and_authorize_resource,
  model: Post,
  as: :custom_name,
  except: [:new, :create],
  preload: [:comments],
  error_handler: CustomErrorHandler

load_resource(atom, params, unsigned_params, socket, opts)

(since 2.0.0)

Loads the resource and assigns it to the socket. When resource is required it will halt the socket if the resource is not found.

load_resource wrapper for attached hook functions, similar to the load_resource/2 plug but for LiveView events on :handle_params and :handle_event stages.

Required opts:

  • :model - Specifies the module name of the model to load resources from
  • :on - Specifies the LiveView lifecycle stages to attach the hook. Default :handle_params

Optional opts:

  • :only - Specifies which actions to authorize

  • :except - Specifies which actions for which to skip authorization

    For handle_params it uses socket.assigns.live_action as :action. For handle_event it uses the event name as :action.

  • :as - Specifies the resource_name to use in assigns

  • :preload - Specifies association(s) to preload

  • :id_name - Specifies the name of the id in params, defaults to "id"

  • :id_field - Specifies the name of the ID field in the database for searching :id_name value, defaults to "id".

  • :required - Specifies if the resource is required, when it's not found it will halt the socket

  • :not_found_handler - Specify a handler function to be called if the resource is not found

Example:


mount_canary :load_resource,
  model: Post,
  only: [:show, :edit, :update],
  preload: [:comments]

mount_canary :load_resource,
  on: [:handle_params, :handle_event]
  model: Post,
  as: :custom_name,
  except: [:new, :create],
  preload: [:comments],
  not_found_handler: {ErrorHandler, :not_found_handler}

mount_canary(type, opts)

(since 2.0.0) (macro)

Mount canary authorization hooks on the current module. It creates a wrapper function to handle_params and handle_event, and attaches the hooks to the Live View.

Example

  mount_canary :load_and_authorize_resource,
    model: Post,
    only: [:edit: :update]