View Source Rules and Checks

Minimal example

Policy definition

Policy module

defmodule MyApp.Policy do
  use LetMe.Policy

  object :article do
    action :create do
      allow role: :writer
    end
  end
end

Check module

defmodule MyApp.Policy.Checks do
  def role(%MyApp.User{role: role}, _object, role), do: true
  def role(_, _, _), do: false
end

Context module

defmodule MyApp.Blog do
  alias MyApp.Blog.Article
  alias MyApp.Policy

  def create_article(params, %MyApp.User{} = current_user) do
    with :ok <- Policy.authorize(:article_create, current_user) do
      %Article{}
      |> Article.changeset(params)
      |> Repo.insert()
    end
  end
end

Check examples

Check without options

Policy module

object :article do
  action :create do
    allow :writer
  end
end

Check implementation

def writer(%MyApp.User{role: :writer}, _object, _opts), do: true
def writer(_, _, _), do: false

Check with options

Policy module

object :article do
  action :delete do
    allow trust_level: 50
  end
end

Check implementation

def trust_level(%MyApp.User{trust_level: actual_level}, _, required_level)
  when actual_level >= required_level,
  do: true

def trust_level(_, _, _), do: false

Check that depends on the object

Policy module

object :article do
  action :update do
    allow :own_resource
  end
end

Check implementation

def own_resource(%MyApp.User{id: user_id}, %{user_id: user_id}, _), do: true
def own_resource(_, _, _), do: false

Rule examples

Multiple actions with the same rules

object :article do
  action [:create, :update, :delete] do
    allow :admin
  end
end

Combine checks with AND

action :create do
  allow [:two_fa_enabled, role: :writer]
end

Combine checks with OR

action :create do
  allow role: :admin
  allow role: :writer
end

Conditionally allow with exception

action :create do
  allow :is_admin
  deny :is_suspended
end

Always allow

action :read do
  allow true
end

Always deny

action :read do
  deny true
end

Always allow with exception

action :read do
  allow true
  deny :user_is_suspended
end

Add description

action :create do
  desc "allows a user to create a new article"
  allow role: :writer
end

Pre-hooks

Without options

Policy module

object :article do
  action :create do
    pre_hooks :preload_roles

    allow role: :admin
    allow role: :editor
    allow role: :writer
  end
end

Check module

def role(%MyApp.User{roles: roles}, _object, role) do
  Enum.any?(roles, & &1.id == role)
end

def role(_, _, _), do: false

def preload_roles(subject, object) do
  {MyApp.Repo.preload(subject, [:roles]), object}
end

With options

Policy module

object :article do
  action :create do
    pre_hooks {:preload_roles, force: true}

    allow role: :admin
    allow role: :editor
    allow role: :writer
  end
end

Check module

def role(%MyApp.User{roles: roles}, _object, role) do
  Enum.any?(roles, & &1.id == role)
end

def role(_, _, _), do: false

def preload_roles(subject, object, opts) do
  {MyApp.Repo.preload(subject, [:roles], opts), object}
end

From a different module

object :article do
  action :create do
    pre_hooks {MyApp.Policy.Prehooks, :preload_roles, force: true}

    allow role: :admin
    allow role: :editor
    allow role: :writer
  end
end

Multiple pre-hooks

object :article do
  action :create do
    pre_hooks [:preload_roles, :role_list_to_role_id_list]

    allow role: :admin
    allow role: :editor
    allow role: :writer
  end
end

Metadata

object :article do
  action :create do
    allow role: :admin
    allow role: :editor
    allow role: :writer
    
    metadata :gql_exclude, true
    metadata :desc_ja, "ユーザーが新しい記事を作成できるようにする"
  end
end