curiosum-dev / permit

An uniform authorization library for Elixir. Supports Plug and Phoenix LiveView, aims for much more.
109 stars 0 forks source link
authorization elixir elixir-lang

Permit

Hex version badge Actions Status Code coverage badge License badge

Plain-Elixir, DSL-less, extensible authorization library for Elixir, also leveraging the power of Ecto, Phoenix and LiveView.

Purpose and usage

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.

Configure & define your permissions

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

Set up your controller

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

Set up your LiveView

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.

Roadmap

An outline of our development goals for both the "MVP" and further releases.

Milestone 1

Further ideas

Installation

If 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.

Contact

Curiosum