README.md

# Sheriff

A simple minimal-dependency way to manage policy based authorization.

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:

  1. Add `sheriff` to your list of dependencies in `mix.exs`:

    ```elixir
    def deps do
      [{:sheriff, "~> 0.2"}]
    end
    ```

  2. Ensure `sheriff` is started before your application:

    ```elixir
    def application do
      [applications: [:sheriff]]
    end
    ```

## Current User

Sheriff defaults to looking in `Plug.Conn.private` for `:current_user`, but this may not be compatible with all appliaction so we can configure the key:

```elixir
config Sheriff,
  resource_key: :your_desired_resource_key
```

## Resource Loading

Resource loaders are responsible for retrieving the targetted resource provided for a specific request.  A global loader can be can be specified in your application configuration or individual loaders can be supplied on a per plug basis.

Sheriff ships with a convenient `Sheriff.ResourceLoader` behaviour:

```elixir
defmodule Example.UserLoader do
  @behaviour Sheriff.ResourceLoader

  def fetch_resource(:show, %{"id" => id}), do: Repo.get(User, id)
  def fetch_resource(:index, _params), do: Repo.all(User)
end

```

## Policies

In Sheriff authorization is handled with policies which are modules that implement the `Sheriff.Policy` behaviour:

```elixir
defmodule Example.UserPolicy do
  @behaviour Sheriff.Policy

  alias Example.User

  # Admins can see all the things!
  def permitted?(%User{role: "admin"}, _request, _resource), do: true

  # Users can access themselves
  def permitted?(%User{id: id}, _request, %User{id: id}), do: true

  # Team admin can view team members
  def permitted?(%User{role: "team_admin", team_id: id}, :show, resources) do
    Enum.all?(resources, &(&1.team_id == team_id)
  end

  # Not match, no access
  def permitted?(_, _, _), do: false
end
```

## Plugs

There are two plugs that serve as the workhorses of Sheriff, these need to occur __after__ `Plug.Parser`:

+ `Sheriff.Plug.LoadResource` - Uses the configured `ResourceLoader` to fetch the target resource
+ `Sheriff.Plug.EnforcePolicy` - Apply a given `Policy` against the current user, target resource, and request.

When defining our authorization pipeline we could use something like this:

```elixir
plug Sheriff.Plug.LoadResource, loader: Example.UserLoader
plug Sheriff.Plug.EnforcePolicy, policy: Example.UserPolicy
```

## Error Handling

Sheriff has just three error scenerios we need to address:

+ The requested resource is missing
+ The current user is not authenticated
+ The current user is not authorized to perform the requested action

To handle these cases we'll want to provide an error handler for Sheriff.  Our handler can be and module that
implements `resource_missing/1`, `unauthenticated/1`, and `unauthorized/1`; the `Sheriff.Handler` behaviour is optional.

Sheriff makes no assumptions so we need to tell it which module to use as a handler:

```elixir
config Sheriff,
  handler: YourApp.ErrorHandler
```

That's it!