README.md

# Gardien

[![CircleCI](https://circleci.com/gh/rpelyush/gardien/tree/master.svg?style=svg)](https://circleci.com/gh/rpelyush/gardien/tree/master)
[![Hex.pm Version](http://img.shields.io/hexpm/v/gardien.svg?style=flat)](https://hex.pm/packages/gardien)

Simple, protocol based authorization, for Phoenix projects.

[Gardien](https://hex.pm/packages/gardien) | [Documentation](https://hexdocs.pm/gardien)

### Installation

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

```elixir
def deps do
  [{:gardien, "~> 1.0.0"}]
end
```

Then run mix `deps.get` to fetch the dependencies.

### Configuration

By default Gardien will try to extract user from `conn.assigns` using `current_user` key.
In case you want to change this behaviour, you can configure `user` as follows:

```elixir
config :gardien, user: :admin
```
or

```elixir
config :gardien, user: {MyHelpers, :user}
```

where `user` is a function that takes `conn` as an argument, e.g:

```elixir
defmodule MyHelpers do
  def user(conn) do
    # return current user
  end
end
```

Gardien comes with default handler that will raise `Gardien.AuthorizationError` in
case user is not authorized to perform some action. You can overwrite default handler as follows:

```elixir
config :gardien,
  unauthorized_handler: {MyHelpers, :unauthorized_handler}
```

where `:unauthorized_handler` is a function that takes `conn` and authorization `context` as arguments, e.g:

```elixir
defmodule MyHelpers do
  def unauthorized_handler(conn, context) do
    # handle not authorized action
  end
end
```

### `Gardien.Policy` protocol

`Gardien.Policy` protocol should be implemented for each domain model (`resource`) that needs to be authorized.
This protocol defines `authorize?(resource, action, user)` function and is used by authorization functions to verify whether `user` is allowed to perform some `action` on a given `resource`.
`Gardien.Policy.authorize?/3` should return `true` or `false`.

**Note:** Gardien comes with a `Gardien.Authorize` module, that can be `use`-d in order to implement a more descriptive policy.

`Gardien.Policy` implementation example (with `Gardien.Authorize`):

```elixir
defimpl Gardien.Policy, for: MyApplication.Post do
  use Gardien.Authorize

  def edit(resource, user) do
    user.id == resource.user_id
  end

  # ...
end

```
In case you're building a closed system, where only logged in users are able to do anything,
you can define your own `Authorize` as follows:

```elixir
defmodule MyApplication.Authorize do
  defmacro __using__(_opts) do
    def authorize?(_resource, _action, user) when is_nil(user) do
      false
    end
    def authorize?(resource, action, user) do
      apply(__MODULE__, action, [resource, user])
    end
  end
end
```

If you want to implement a policy without a corresponding model, one way to
do that would be to define `Gardien.Policy` implementation for `Atom`, e.g:

```elixir
defimpl Gardien.Policy, for: Atom do
  def authorize?(:dashboard, :view, _user) do
    false
  end
end
```

### Overview

Define policy:

```elixir
defimpl Gardien.Policy, for: MyApplication.Post do
  use Gardien.Authorize

  def edit(post, user) do
    post.user_id == user.id
  end
end
```

Use in controller:

```elixir
defmodule MyApplication.PostController do
  import Gardien.Controller

  def edit(conn, params) do
    post = Repo.get(Post, params["id"])

    authorize post, conn, fn ->
      render conn, "edit.html", post: post
    end
  end
end
```

Use as a plug:

```elixir
defmodule MyApplication.PostController do
  # authorization plug expects `post` to be present is `conn.assigns`
  plug Gardien.Authorization, resource: :post

  def edit(conn, params) do
    render conn, "edit.html", post: post
  end
end
```

Use in view/template:

```elixir
defmodule MyApplication.PostView do
  import Gardien.View, only: [authorize?: 3]
end

# in template
<%= if authorize?(post, :edit, @conn) do %>
  <%= link "Edit", to: post_path(@conn, :edit, post) %>
<% end %>
```