View Source 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
:current_user
You need to assign the current user to the socket before LiveGuard
on_mount/4
callback is called.. The default assign name for the current user is:current_user
. If you assign the current user as another than:current_user
you can set in the config:config :live_guard, :current_user, :user
:unauthorized_handler
This function handles unauthorized LiveView lifecycle stages. It's called when the
allowed?/4
function returnsfalse
.By default it will put an error flash message with text "You don't have permission to do that!".
:mount
and:handle_params
LiveView lifecycle stages needs redirect after it detected as unauthorized. In this case by default it will redirect to the home page (/
).You can set a custom handler in the config:
config :live_guard, :unauthorized_handler, {MyModule, :my_handle_unauthorized}
It's called with 2 inputs, first is a
socket
, second isis_redirect
(boolean).
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:
- with
live_session/3
to protect a couple of LiveViews lifecycle stages at once:# lib/my_app_web/router.ex defmodule MyAppWeb.Router do use MyAppWeb, :router live_session :default, on_mount: LiveGuard do # routes... end end
- if you want to protect LiveView lifecycle stages in every LiveViews, you can achive that with the following code:
# lib/my_app_web.ex defmodule MyAppWeb do # some code... def live_view do quote do use Phoenix.LiveView, layout: {MyAppWeb.Layouts, :app} on_mount LiveGuard unquote(html_helpers()) end end # some code... end
- and if you want to protect an individual LiveView lifecycle stages you can achive that with the following code:
# lib/my_app_web/live/my_module_live.ex defmodule MyAppWeb.MyModuleLive do use MyAppWeb, :live_view on_mount LiveGuard def mount(_params, _session, socket) do {:ok, socket} end # some code... end
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 returnstrue
. 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
This package is inspired by canary.