Zephyr
Elixir authorization system based on the ReBAC (Relationship-based Access Control) model
Zephyr is an authorization library based on Google's Zanzibar. Zephyr though based on Zanzibar, its semantics for defining objects and access control closely follows Authzed's SpiceDB.
To know more about how ReBAC authorization works, click on the Zanzibar and SpiceDB links above.
Installation
If available in Hex, the package can be installed
by adding zephyr to your list of dependencies in mix.exs:
def deps do
[
{:zephyr, "~> 1.0.0-alpha.1"}
]
endUsage
-
Generate a migration for relation tuples. You may use
mix ecto.gen.migration create_relations. You may choose any filename you want.
defmodule MyApp.Repo.Migrations.CreateRelations do
use Ecto.Migration
def up, do: Zephyr.Migrations.up(subject_key_type: :integer, object_key_type: :integer)
def down, do: Zephyr.Migrations.down()
end
-
Add your objects. You may write the objects on any filename and path as long as they're
included in the compilation and
Zephyr.Definitionis imported. For example,lib/my_app/definitions.ex:
import Zephyr.Definition
# Defines an object called users
definition(:users)
# Defines an object called organizations
definition :organizations do
# Member of organizations are user objects
relation(:member, :users)
end
# Defines an object called repositories
definition :repositories do
# Maintainer of repositories are user objects
relation(:maintainer, :users)
# Parent of repository is an organization
relation(:parent_org, :organizations)
# Repositories can be read by maintainers or member of the parent organization
relation(:reader, :maintainer + (:parent_org > :member))
end
# Defines an object called issues
definition :issues do
# Users are creator of issues
relation(:creator, :users)
# Issues are under a parent repository
relation(:parent_repository, :repositories)
# Creators and reader of the parent repository can close issues
relation(:closer, :creator + (:parent_repository > :reader))
endObjects should always refer to tables of the resources in your application.
-
You should now be able to write relations in your app via the
write/2API. For example:
# For example below are users, an issue, and its parent org retrieved from the database
alice = %MyApp.User{id: 1}
bob = %MyApp.User{id: 2}
repository = %MyApp.Repository{id: 3}
issue = %MyApp.Issue{id: 4}
# The two arguments in the write/2 API refers to subject and object, respectively.
# They can be read as follows:
# User with id 1 (the subject) is the creator (object predicate) of Issue with id 4
{:ok, auth} = Zephyr.write({"users", alice.id, nil}, {"issues", issue.id, "creator"})
# User with id 2 is a maintainer of Repository with id 3
{:ok, auth} = Zephyr.write({"users", bob.id, nil}, {"repositories", org.id, "maintainer"})
# Repository with id 3 is the parent repository of Issue with id 4
{:ok, auth} = Zephyr.write({"repositories", repository.id, nil}, {"issues", issue.id, "parent_repository"})-
As seen in the issues definition above
Creators and reader of the parent repository can close issues
This means Alice and Bob should have a relation closer to issue because Alice created it and Bob is a maintainer of the issue's parent repository. To check we can use the
check/3 API:
iex> Zephyr.check(issue, "closer", alice)
true
iex> Zephyr.check(issue, "closer", bob)
true
iex> Zephyr.check(issue, "closer", user_neither_creator_nor_maintainer)
falseOperators
There are 4 supported operators to define policies:
Union (+) Unions are read as "or". For example
relation(:commenter, :editor + :reader)means an object with this policy has relationcommenterif subject has relationeditororreaderto the object.Exclusion (-) Excludes the righthand result from the lefthand sets of subjects. For example
relation(:commenter, :editor - :reader)means if Alice has relation editor and reader, Bob has editor relation only, and Charlie has reader relation only. Then Alice and Charlie will be excluded and Bob will only have the access.Intersection (&&) Intersections are read as "and". For example
relation(:commenter, :editor && :reader)means an object with this policy has relationcommenterif subject has both relationeditorandreader.Walk (>) This traverses the relation from the object itself to another object as seen in the example
relation(:closer, :creator + (:parent_repository > :reader)). Always wrap them in parenthesis.
:_this object
:_this refers to direct relations. For example relation(:editor, :_this + (:parent > :maintainer))
is similar to relation(:editor, :editor + (:parent > :maintainer))
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/zephyr.