Plain-Elixir, DSL-less, extensible authorization library for Elixir, also leveraging the power of Ecto, Phoenix and LiveView.
Provide a single source of truth of action permissions throughout your codebase, making use of Ecto to have your Phoenix Controllers and LiveViews authorize access to resources without having to repeat yourself.
If you join the monorepo bandwagon, you should be able to nicely drop your authorization into whatever's driven by Plug (Phoenix controllers) as well as into Phoenix LiveView, and perhaps even more - because it's very likely that your codebase will use multiple frameworks to process data that requires authorization.
defmodule MyApp.Authorization do
use Permit, permissions_module: MyApp.Permissions
end
defmodule MyApp.Permissions do
use Permit.Permissions, actions_module: Permit.Phoenix.Actions
def can(%{role: :admin} = user) do
permit()
|> all(MyApp.Blog.Article)
end
def can(%{id: user_id} = user) do
permit()
|> all(MyApp.Blog.Article, id: user_id)
|> read(MyApp.Blog.Article) # allows :index and :show
end
def can(user), do: permit()
end
defmodule MyAppWeb.Blog.ArticleController do
use MyAppWeb, :controller
use Permit.Phoenix.Controller,
authorization_module: MyApp.Authorization,
resource_module: MyApp.Blog.Article
# assumption: current user is in assigns[:current_user] - this is configurable
def show(conn, _params) do
# At this point, the Article has already been preloaded by Ecto and checked for authorization
# based on action name (:show).
# It's available as the @loaded_resource assign.
render(conn, "show.html")
end
def show(conn, _params) do
# The list of Articles accessible by current user has been preloaded by Ecto
# into the @loaded_resources assign.
render(conn, "index.html")
end
# Optionally, implement the handle_unauthorized/1 callback to deal with authorization denial.
end
defmodule MyAppWeb.Router do
use Phoenix.Router
import Phoenix.LiveView.Router
live_session :authenticated, on_mount: Permit.Phoenix.LiveView.AuthorizeHook do
live("/articles", MyAppWeb.Blog.ArticlesLive, :index)
live("/articles/:id", MyAppWeb.Blog.ArticlesLive, :show)
end
end
defmodule MyAppWeb.Blog.ArticleLive do
use Phoenix.LiveView
use Permit.Phoenix.LiveView,
authorization_module: MyAppWeb.Authorization,
resource_module: MyApp.Blog.Article
@impl true
def fetch_subject(session), do: # load current user
# Both in the mount/3 callback and in a hook attached to the handle_params event,
# authorization will be performed based on assigns[:live_action].
# Optionally, implement the handle_unauthorized/1 callback to deal with authorization denial.
end
The library idea was originally briefed and announced in Michal Buszkiewicz's Curiosum Elixir Meetup #5 in 2022.
An outline of our development goals for both the "MVP" and further releases.
show
, update
)index
)index
)permit_ecto
and permit_phoenix
libraries providing the possibility of using the library without unneeded dependenciesIf available in Hex, the package can be installed
by adding permit
to your list of dependencies in mix.exs
:
def deps do
[
{:permit, "~> 0.2.1"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/permit.