LivePanels.Canvas.Policy behaviour (livepanels v0.1.0)

Copy Markdown View Source

Application policy contract for canvas commands.

Runtime authorization verifies LivePanels invariants such as payload shape, actor role, item ownership, leases, and declared capabilities. A configured policy module can reject otherwise valid commands with application rules and attach decision metadata to accepted commands.

Configure a policy on the canvas runtime:

on_mount {LivePanels.Canvas, pubsub: MyApp.PubSub, policy: MyApp.CanvasPolicy}

Policy modules implement authorize/1:

defmodule MyApp.CanvasPolicy do
  @behaviour LivePanels.Canvas.Policy

  @impl true
  def authorize(%LivePanels.Canvas.Policy.Request{action: :remove_item} = request) do
    if MyApp.Workflows.locked?(request.canvas_id, request.item_ids, request.actor) do
      {:error, :workflow_locked}
    else
      :ok
    end
  end

  def authorize(_request), do: :ok
end

Returning :ok accepts the command. Returning {:ok, metadata} accepts the command and stores metadata under :policy in command metadata. Returning {:error, reason} or {:halt, reason} rejects the command.

Policy callbacks run in the reducer/coordinator command path behind a timeout boundary. Configure timeout_ms: when command acceptance checks need a different bound:

on_mount {LivePanels.Canvas,
  pubsub: MyApp.PubSub,
  policy: {MyApp.CanvasPolicy, timeout_ms: 2_000}}

A timeout rejects the LivePanels command. Policy callbacks should be bounded acceptance checks, not application write transactions or side-effect hooks.

Summary

Types

metadata()

@type metadata() :: map() | keyword()

result()

@type result() :: :ok | {:ok, metadata()} | {:error, term()} | {:halt, term()}

t()

@type t() :: %LivePanels.Canvas.Policy{
  module: module() | nil,
  opts: map(),
  timeout_ms: timeout_ms()
}

timeout_ms()

@type timeout_ms() :: pos_integer() | :infinity

Callbacks

authorize(t)

@callback authorize(LivePanels.Canvas.Policy.Request.t()) :: result()