<div align="center">

# `rbac`

Role Based Access Control (**`RBAC`**) gives you
a human-friendly way of controlling access
to specific data/features in your App(s).

![GitHub Workflow Status](
[![ dependency status](](
[![contributions welcome](](


## Why?

You want an _easy_ way to restrict access to features for your Elixir/Phoenix App
based on a sane model of roles.
**`RBAC`** lets you _easily_ manage roles and permissions in any application
and see at a glance exactly which permissions a person has in the system.
It reduces complexity over traditional
Access Control List (ACL) based permissions systems.

## What?

The purpose of **`RBAC`** is to provide a framework
for application administrators and developers
to manage the permissions assigned to the people using the App(s).

## Who?

Anyone who is interested in developing secure applications
used by many people with differing needs and permissions
should learn about **`RBAC`**.

## _How_?

### Installation

Install by adding `rbac` to your list of dependencies in `mix.exs`:

def deps do
    {:rbac, "~> 0.6.0"}

<br />

### Initialize Your Roles List (Cache)

In order to use **`RBAC`** you need to initialize
the _in-memory cache_ with a list of roles.

#### Got your Own List of Roles?

If you prefer to manage your own list of roles
you can simply supply your own list of roles e.g:

roles = [%{id: 1, name: "admin"}, %{id: 2, name: "subscriber"}]

To initialize the list of roles _once_ (_at boot_) for your Phoenix App,
open the `application.ex` file of your project
and locate the `def start(_type, _args) do` definition, e.g:

def start(_type, _args) do
  # List all child processes to be supervised
  children = [
    # Start the Ecto repository
    # Start the endpoint when the application starts
    {Phoenix.PubSub, name: App.PubSub},
    # Starts a worker by calling: Auth.Worker.start_link(arg)
    # {Auth.Worker, arg},

  # See
  # for other strategies and supported options
  opts = [strategy: :one_for_one, name: App.Supervisor]
  Supervisor.start_link(children, opts)

Add the following code at the top of the `start/2` function definition:

# initialize RBAC Roles Cache:
roles = [%{id: 1, name: "admin"}, %{id: 2, name: "subscriber"}]

#### Using `auth` to Manage Roles?

**`RBAC`** is _independent_ from our
[`auth`]( App
and it's corresponding helper library

However if you want a ready-made list of universally applicable roles
and an _easy_ way to manage and create custom roles for your App,
**`auth`** has you covered:

Once you have exported your
`AUTH_API_KEY` Environment Variable
following these instructions:

You can source your list of roles
and initialize it
with the following code:

# initialize RBAC Roles Cache:

expects the `AUTH_API_KEY` Environment Variable to be set.

<br />

### Usage

Once you have added the initialization code,
you can easily check that a person has a required role
using the following code:

# role argument as String
RBAC.has_role?([2], "admin")
> true

# role argument as Atom
RBAC.has_role?([2], :admin)
> true

# second argument (role) as Integer
RBAC.has_role?([2], 2)
> true

The first argument is a `List` of role ids.
The second argument (`role`) can either be
an `String`, `Atom` or`Integer`
corresponding to the `name` of the role
or the `id` respectively.
We prefer using `String` because its more developer/maintenance friendly.
We can immediately see which role is required

Or if you want to check that the person has _any_ role
in a list of potential roles:

RBAC.has_role_any?([2,4,7], ["admin", "commenter"])
> true

RBAC.has_role_any?([2,4,7], [:admin, :commenter])
> true

### Using `rbac` with `auth_plug`

If you are using [`auth_plug`](
to handle checking auth in your App.
It adds the `person` map to the `conn.assigns` struct.
That means the person's roles are listed in:

  app_id: 8,
  auth_provider: "github",
  email: "",
  exp: 1631721211,
  givenName: "Alex",
  id: 772,
  roles: "2"

For convenience, we allow the first argument
of both `has_role/2` and `has_role_any?/2`
to accept `conn` as the first argument:

RBAC.has_role?(conn, "admin")
> true

Check that the person has has any role in a list of potential roles:

RBAC.has_role_any?(conn, ["admin", "commenter"])
> true

We prefer to make our code as declarative and human-friendly as possible,
hence the `String` role names.
However both the role-checking functions also accept a list of integers,
corresponding to the `` of the required role, e.g:

RBAC.has_role?(conn, 2)
> true

If the person does not have the **`superadmin`** role,
`has_role?/2` will return `false`

RBAC.has_role?(conn, 1)
> false

Or supply a list of integers to `has_role_any?/2` if you prefer:

RBAC.has_role_any?(conn, [1,2,3])
> true

You can even _mix_ the type in the list (_though we don't recommend it..._):

RBAC.has_role_any?(conn, ["admin",2,3])
> true

We recommend picking one, and advise using strings for code legibility.

RBAC.has_role?(conn, "building_admin")

Is very clear which role is required.
Whereas using an `int` (_especially for custom roles_) is a bit more terse:

RBAC.has_role?(conn, 13)

It requires the developer/code reviewer/maintainer
to either know what the role is,
or look it up in a list.
Stick with `String` as your role names in your code.

API/Function reference available at

## Trouble Shooting

If your app does not have a valid `AUTH_API_KEY` you may see the following error:

Generated auth app
** (Mix) Could not start application auth: exited in: Auth.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (Protocol.UndefinedError) protocol Enumerable not implemented for "Internal Server Error" of type BitString. This protocol is implemented for the following type(s): Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.PrepareStream, DBConnection.Stream, StreamData, IO.Stream, Map, Date.Range, List, GenEvent.Stream, HashSet, MapSet, Range, HashDict, Function, Stream, File.Stream
            (elixir 1.10.4) lib/enum.ex:1: Enumerable.impl_for!/1
            (elixir 1.10.4) lib/enum.ex:141: Enumerable.reduce/3
            (elixir 1.10.4) lib/enum.ex:3383:
            (rbac 0.4.0) lib/rbac.ex:69: RBAC.parse_body_response/1
            (rbac 0.4.0) lib/rbac.ex:88: RBAC.init_roles_cache/2
            (auth 1.2.4) lib/auth/application.ex:9: Auth.Application.start/2
            (kernel 7.0) application_master.erl:277: :application_master.start_it_old/4
The command "mix ecto.setup" failed and exited with 1 during .

Simply follow the instructions to get your `AUTH_API_KEY` and export it as an environment variable.

<br /><br />

## tl;dr > RBAC Knowledge Summary

Each role granted just enough flexibility and permissions
to perform the tasks required for their job,
this helps enforce the
[principal of least privilege](

The RBAC methodology is based on a set of three principal rules
that govern access to systems:

1. **Role Assignment**:
Each transaction or operation can only be carried out
if the person has assumed the appropriate role.
An operation is defined as any action taken
with respect to a system or network object that is protected by RBAC.
Roles may be assigned by a separate party
or selected by the person attempting to perform the action.

2. **Role Authorization**:
The purpose of role authorization
is to ensure that people can only assume a role
for which they have been given the appropriate authorization.
When a person assumes a role,
they must do so with authorization from an administrator.

3. **Transaction Authorization**:
An operation can only be completed
if the person attempting to complete the transaction
possesses the appropriate role.

## Recommended Reading