View Source Authorizir behaviour (Authorizir v2.0.0-alpha.6)
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 Types
@type to_ext_id() :: Authorizir.ToAuthorizirId.t()
Link to this section Callbacks
@callback init() :: :ok
Link to this section Functions
@spec add_child(Ecto.Repo.t(), to_ext_id(), to_ext_id(), module()) :: :ok | {:error, :invalid_parent | :invalid_child}
@spec allow_permission(Ecto.Repo.t(), to_ext_id(), to_ext_id(), to_ext_id()) :: :ok | {:error, atom()}
deny_permission(repo, subject_id, object_id, permission_id, static \\ false)
View Sourcegrant_permission(repo, subject_id, object_id, permission_id, static \\ false)
View Source@spec permission_granted?(Ecto.Repo.t(), to_ext_id(), to_ext_id(), to_ext_id()) :: :denied | :granted | {:error, :invalid_subject | :invalid_object | :invalid_permission}
@spec register_object(Ecto.Repo.t(), to_ext_id(), String.t(), static :: boolean()) :: :ok | {:error, :description_is_required | :id_is_required}
@spec register_permission(Ecto.Repo.t(), to_ext_id(), String.t(), static :: boolean()) :: :ok | {:error, :description_is_required | :id_is_required}
@spec register_subject(Ecto.Repo.t(), to_ext_id(), String.t(), static :: boolean()) :: :ok | {:error, :description_is_required | :id_is_required}
@spec remove_child(Ecto.Repo.t(), to_ext_id(), to_ext_id(), module()) :: :ok | {:error, :invalid_parent | :invalid_child}
@spec revoke_permission(Ecto.Repo.t(), to_ext_id(), to_ext_id(), to_ext_id()) :: :ok | {:error, atom()}