LiveGuard

A simple package to protect the LiveView lifecycle stages such as :mount, :handle_params, :handle_event, :handle_info and :handle_async.

Installation

For the latest master:

def deps do
  [
    {:live_guard, github: "FabianDaniel00/live_guard"}
  ]
end

For the latest release:

def deps do
  [
    {:live_guard, "~> 0.1.8"}
  ]
end

Then run mix deps.get to fetch the dependencies.

Config

Usage

LiveGuard provide an on_mount/4 callback which can be used in Phoenix LiveViews. Read the docs.

Since this is an on_mount/1 callback you can use it three ways:

Implementation

For now you should ask, okay but how it will know how to protect the LiveView lifecycle stages?

You need to implement allowed?/4 protocol functions. The first input of allowed?/4 function is the user, the second is the LiveView module, the third is the LiveView lifecycle stage and the last is LiveView lifecycle stage inputs. In this way you can pattern match to your needings. You can put this file anywhere but /lib/my_app_web/live/abilities.ex is recommended.

It must return a boolean.

# /lib/my_app_web/live/abilities.ex

defimpl LiveGuard.Allowed, for: User do
  @before_compile {LiveGuard, :before_compile_allowed}

  def allowed?(
        %User{role: role},
        MyModuleLive,
        :handle_event,
        {"delete_item", _unsigned_params, _socket}
      )
      when role in [:viewer, :customer],
      do: false

  # other `allowed?/4` functions...
end

Note: As you can see, you don't have to define catch-all allowed?/4 function because we used @before_compile {LiveGuard, :before_compile_allowed} hook. It returns true. This is optional.

If the user is not authenticated you can add the following implementation as below:

defimpl LiveGuard.Allowed, for: Atom do
  @before_compile {LiveGuard, :before_compile_allowed}

  def allowed?(nil, MyModuleLive, :handle_event, {"delete_item", _unsigned_params, _socket}),
    do: false

  # other `allowed?/4` functions...
end

Optimization (optional)

By default if you use the on_mount/4 callback of LiveGuard, it will attach hooks to attachable LiveView lifecycle stages (:handle_params, :handle_event, :handle_info and :handle_async). If you need to protect for example only the :handle_event LiveView lifecycle stage for an individual LiveView module you can use this function. You can put this file anywhere but /lib/my_app_web/live/guarded_stages.ex is recommended.

It must return a list of valid attachable LiveView lifecycle stages (unless :after_render).

Example

# /lib/my_app_web/live/guarded_stages.ex

defimpl LiveGuard.GuardedStages, for: Atom do
  @before_compile {LiveGuard, :before_compile_guarded_stages}

  def guarded_stages(MyModuleLive), do: [:handle_event]

  # other `guarded_stages?/1` functions...
end

In this case it will only attach hook to :handle_event LiveView lifecycle stage.

Note: As you can see, you don't have to define catch-all guarded_stages/1 function because we used @before_compile {LiveGuard, :before_compile_guarded_stages} hook. It returns the valid attachable LiveView lifecycle stages (:handle_params, :handle_event, :handle_info and :handle_async). This is optional.

License

MIT License. Copyright 2023 Daniel Fabian.

Few words from the author

GitHub repository

This package is inspired by canary.