# Can
> Dead simple, fire and forget authorization kit for the Phoenix framework
[![Build Status](https://semaphoreci.com/api/v1/imranismail/can/branches/master/badge.svg)](https://semaphoreci.com/imranismail/can)
## Installation
Add Can to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:can, "~>0.0.3"}]
end
```
## Usage
Generally, there are two things you need to explicitly implement in your application.
For this controller and action
```elixir
defmodule MyApp.PageController do
def show(conn, %{"id" => id}) do
page = Repo.get(Page, id)
render(conn, "show.html", page: page)
end
end
```
Step 1: Use the can macro and add an unauthorized_handler
```elixir
defmodule MyApp.PageController do
use MyApp.Web, :controller
use Can, :unauthorized_handler
def show(conn, %{"id" => id}) do
page = Repo.get(Page, id)
can(conn, page) do
render(conn, "show.html", page: page)
end
end
def unauthorized(conn, resource, policy) do
conn
|> put_flash(:error, "You are unauthorized because #{policy} did not return true for author id #{resource[:author_id]}")
|> render("show.html", page: resource)
end
end
```
Step 2: Add the policy module and function
Can will try to find the policy based on the second argument and the following pattern, therefore we need to adhere
to a convention set by Phoenix
- if no argument or `nil` is passed -> the policy will be based off the controller's name
- if changeset or model struct is passed -> the policy will be based off the model's name
```elixir
def MyApp.PagePolicy do
def show(conn, page) do
conn.assign.current_user.id == page[:author_id]
end
end
```
### Alternative Handler
The unauthorized handler can also be done in a separate module if you wish so.
This effectively separates the handler and the controller, and makes pattern matching against the policy clean, readable and reusable.
```elixir
defmodule MyApp.PageController do
use MyApp.Web, :controller
use Can, {MyApp.UnauthorizedHandler, :unauthorized}
def show(conn, %{"id" => id}) do
page = Repo.get(Page, id)
can(conn, page) do
render(conn, "show.html", page: page)
end
end
end
defmodule MyApp.UnauthorizedHandler do
import Phoenix.Controller
def unauthorized(conn, resource, PagePolicy) do
conn
|> put_flash(:error, "You are unauthorized because #{policy} did not return true for author id #{resource[:author_id]}")
|> render("show.html", page: resource)
end
# wildcard
def unauthorized(conn, _resource, policy) do
conn
|> put_flash(:error, "You are unauthorized because #{policy} did not return true")
|> render(MyApp.ErrorView, "401.html")
end
end
```
You can write your own authorization logic as complex or as simple as you wish. It is necessary however, at the end of your authorization logic, it has to return a boolean value.
In the case when the authorization logic returns true, the connection proceeds normally. If the authorization logic return false, the unauthorized handler will be called instead.
## Documentation
See [documentation](http://hexdocs.pm/can/) on hexdocs for API reference and usage details.