# 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 %>
```