View Source Authorizir behaviour (Authorizir v2.0.0-alpha.2)

Ecto-backed Authorization Library for Elixir Applications

See README for a description of the mathematical model used as the basis of this system.

usage

Usage

Imagine you are creating an app that handles online ordering.

First, create your app's authorization module, configuring it with your application repository:

defmodule Auth do
  use Authorizir, repo: Repo
end

Users of the application might be organized into a hierarchy as follows (note that an employee can also be a customer):

graph TD
  *[Users *] --> E[Employees]
  * --> C[Customers]

  E --> A[Admins]
  E --> M[Marketing]
  E --> F[Finance]
  E --> S[Shipping and Fulfillment]

  A --> Bob

  M --> Bob
  M --> Jane

  F --> Amanda

  S --> George
  S --> Beth

  C --> Amanda
  C --> George
  C --> John

We have two types of Subject entities represented here; "Organizational Units" represent groups of users such as internal departments and customers, while "Users" represent the individual system accounts. Each of these are represented with Ecto schemas in our app, and we include the Authorizir.Subject behavior in the modules, so that they can participate in the Subject hierarcy.

First we add the necessary migrations by running mix ecto.gen.migraion add_org_units_and_users and editing the resulting migration file:

defmodule AddOrgUnitsAndUsers do
  use Ecto.Migration
  import Authorizir.Migrations, only: [apply_subject_hierarchy: 2]

  create table("org_units") do
    add :name, :string, null: false
    timestamps()
  end
  apply_subject_hierarchy("org_units", id_field: :id)

  create table("users") do
    add :name, :string, null: false
    timestamps()
  end
  apply_subject_hierarchy("users", id_field: :id)
end
defmodule OrgUnit do
  use Ecto.Schema
  use Authorizir.Subject

  schema "org_units" do
    field :name, :string
  end
end

defmodule User do
  use Ecto.Schema
  use Authorizir.Subject

  schema "users" do
    field :name, :string
  end
end

You can create the hierarchy as:

{:ok, employees} = %OrgUnit{name: "Employees"} |> Repo.insert()

{:ok, customers} = %OrgUnit{name: "Customers"} |> Repo.insert()

{:ok, admins} = %OrgUnit{name: "Admins"} |> Repo.insert()
:ok = Auth.add_child(employees.id, admins.id, Subject)

{:ok, marketing} = %OrgUnit{name: "Marketing"} |> Repo.insert()
:ok = Auth.add_child(employees.id, marketing.id, Subject)

{:ok, finance} = %OrgUnit{name: "Finance"} |> Repo.insert()
:ok = Auth.add_child(employees.id, finance.id, Subject)

{:ok, shipping} = %OrgUnit{name: "Shipping and Fulfillment"} |> Repo.insert()
:ok = Auth.add_child(employees.id, shipping.id, Subject)

{:ok, bob} = %User{name: "Bob"} |> Repo.insert()
:ok = Auth.add_child(admins.id, bob.id, Subject)
:ok = Auth.add_child(marketing.id, bob.id, Subject)

{:ok, jane} = %User{name: "Jane"} |> Repo.insert()
:ok = Auth.add_child(marketing.id, jane.id, Subject)

{:ok, amanda} = %User{name: "Amanda"} |> Repo.insert()
:ok = Auth.add_child(finance.id, amanda.id, Subject)
:ok = Auth.add_child(customers.id, amanda.id, Subject)

{:ok, george} = %User{name: "George"} |> Repo.insert()
:ok = Auth.add_child(shipping.id, george.id, Subject)
:ok = Auth.add_child(customers.id, george.id, Subject)

{:ok, beth} = %User{name: "Beth"} |> Repo.insert()
:ok = Auth.add_child(shipping.id, beth.id, Subject)

{:ok, john} = %User{name: "John"} |> Repo.insert()
:ok = Auth.add_child(customers.id, john.id, Subject)

Link to this section Summary

Link to this section Callbacks

Link to this callback

add_child(parent_id, child_id, type)

View Source
@callback add_child(parent_id :: binary(), child_id :: binary(), type :: module()) ::
  :ok | {:error, reason :: atom()}
Link to this callback

allow_permission(subject_id, object_id, permission_id)

View Source
@callback allow_permission(
  subject_id :: binary(),
  object_id :: binary(),
  permission_id :: binary()
) :: :ok | {:error, reason :: atom()}
Link to this callback

deny_permission(subject_id, object_id, permission_id)

View Source
@callback deny_permission(
  subject_id :: binary(),
  object_id :: binary(),
  permission_id :: binary()
) :: :ok | {:error, reason :: atom()}
Link to this callback

grant_permission(subject_id, object_id, permission_id)

View Source
@callback grant_permission(
  subject_id :: binary(),
  object_id :: binary(),
  permission_id :: binary()
) :: :ok | {:error, reason :: atom()}
@callback init() :: :ok
Link to this callback

permission_declarations()

View Source
@callback permission_declarations() :: [{String.t(), String.t(), [String.t()]}]
Link to this callback

permission_granted?(subject_id, object_id, permission_id)

View Source
@callback permission_granted?(
  subject_id :: binary(),
  object_id :: binary(),
  permission_id :: binary()
) :: :granted | :denied | {:error, reason :: atom()}
Link to this callback

register_object(id, description, static)

View Source
@callback register_object(id :: binary(), description :: String.t(), static :: boolean()) ::
  :ok | {:error, reason :: atom()}
Link to this callback

register_permission(id, description, static)

View Source
@callback register_permission(
  id :: binary(),
  description :: String.t(),
  static :: boolean()
) ::
  :ok | {:error, reason :: atom()}
Link to this callback

register_subject(id, description, static)

View Source
@callback register_subject(id :: binary(), description :: String.t(), static :: boolean()) ::
  :ok | {:error, reason :: atom()}
Link to this callback

remove_child(parent_id, child_id, type)

View Source
@callback remove_child(parent_id :: binary(), child_id :: binary(), type :: module()) ::
  :ok | {:error, reason :: atom()}
Link to this callback

revoke_permission(subject_id, object_id, permission_id)

View Source
@callback revoke_permission(
  subject_id :: binary(),
  object_id :: binary(),
  permission_id :: binary()
) :: :ok | {:error, reason :: atom()}
@callback role_declarations() :: [{String.t(), String.t(), [String.t()]}]

Link to this section Functions

Link to this function

add_child(repo, parent_id, child_id, type)

View Source
@spec add_child(Ecto.Repo.t(), binary(), binary(), module()) ::
  :ok | {:error, :invalid_parent | :invalid_child}
Link to this function

allow_permission(repo, subject_id, object_id, permission_id)

View Source
@spec allow_permission(Ecto.Repo.t(), binary(), binary(), binary()) ::
  :ok | {:error, atom()}
Link to this function

deny_permission(repo, subject_id, object_id, permission_id)

View Source
@spec deny_permission(Ecto.Repo.t(), binary(), binary(), binary()) ::
  :ok | {:error, atom()}
Link to this function

grant_permission(repo, subject_id, object_id, permission_id)

View Source
@spec grant_permission(Ecto.Repo.t(), binary(), binary(), binary()) ::
  :ok | {:error, atom()}
Link to this macro

permission(ext_id, description, opts \\ [])

View Source (macro)
Link to this function

permission_granted?(repo, subject_id, object_id, permission_id)

View Source
@spec permission_granted?(Ecto.Repo.t(), binary(), binary(), binary()) ::
  :denied
  | :granted
  | {:error, :invalid_subject | :invalid_object | :invalid_permission}
Link to this function

register_object(repo, id, description, static \\ false)

View Source
@spec register_object(Ecto.Repo.t(), binary(), String.t(), static :: boolean()) ::
  :ok | {:error, :description_is_required | :id_is_required}
Link to this function

register_permission(repo, id, description, static \\ false)

View Source
@spec register_permission(Ecto.Repo.t(), binary(), String.t(), static :: boolean()) ::
  :ok | {:error, :description_is_required | :id_is_required}
Link to this function

register_subject(repo, id, description, static \\ false)

View Source
@spec register_subject(Ecto.Repo.t(), binary(), String.t(), static :: boolean()) ::
  :ok | {:error, :description_is_required | :id_is_required}
Link to this function

remove_child(repo, parent_id, child_id, type)

View Source
@spec remove_child(Ecto.Repo.t(), binary(), binary(), module()) ::
  :ok | {:error, :invalid_parent | :invalid_child}
Link to this function

revoke_permission(repo, subject_id, object_id, permission_id)

View Source
@spec revoke_permission(Ecto.Repo.t(), binary(), binary(), binary()) ::
  :ok | {:error, atom()}
Link to this macro

role(ext_id, description, opts \\ [])

View Source (macro)