# Conductor
Simple package for authorization.
## Installation
```elixir
def deps do
[{:conductor, "~> 0.1.0"}]
end
```
## Conductor macro
Basically, this:
```elixir
defmodule Controller do
use Conductor
use Phoenix.Controller
def index(conn, _params), do: #...
def show(conn, _params), do: #...
@authorize scope: "scope1"
def create(conn, _params) do: #...
@authorize scope: "scope2"
def delete(conn, _params), do: #...
end
```
will be compiled to this:
```elixir
defmodule Controller do
use Conductor
use Phoenix.Controller
plug Conductor.Plugs.Authorize, "scope1" when action in [:create]
plug Conductor.Plugs.Authorize, "scope2" when action in [:delete]
plug Conductor.Plugs.Authorize, nil when not action in [:create, :delete]
def index(conn, _params), do: #...
def show(conn, _params), do: #...
def create(conn, _params), do: #...
def delete(conn, _params), do: #...
end
```
## Root scope
You can register scope that will have full access everywhere
```elixir
config :conductor,
root_scopes: ["admin"]
```
## Adding scopes
```elixir
conn |> Plug.Conn.assign(:scopes, ["scope1, scope2"])
```
## Public access
```elixir
defmodule Router do
pipeline :public do
plug Conductor.Plugs.SkipAuthorization
end
scope "/public", MyApp do
pipe_through [:public]
get "/something", SomethingController, :something
end
end
```
## Authorization failures
To avoid confusion with random `403` response codes that come from nowhere, Conductor will raise error on authorization failure as default.
This can be changed by following config
```elixir
config :conductor,
on_auth_failure: :send_resp
```
## Example
```elixir
#config
config :conductor,
root_scopes: ["root_scope"],
on_auth_error: :send_resp
#router
pipeline :public do
plug Conductor.Plugs.SkipAuthorization
end
scope "/", Example do
get "/1", Controller, :action1
get "/3", Controller, :action3
get "/4", Controller, :action4
end
scope "/", Example do
pipe_through [:public]
get "/2", Controller, :action2
end
#conns
conn1 = Phoenix.ConnTest.build_conn()
conn2 = conn1 |> Plug.Conn.assign(:scopes, ["scope1", "scope2"])
conn3 = conn1 |> Plug.Conn.assign(:scopes, ["root_scope"])
#endpoints
@authorize scope: "scope1"
def action1(conn, _params), do: conn |> send_resp(200, "")
@authorize scope: "scope2"
def action2(conn, _params), do: conn |> send_resp(200, "")
def action3(conn, _params), do: conn |> send_resp(200, "")
@authorize scopes: ["other", "unused"]
def action4(conn, _params), do: conn |> send_resp(200, "")
```
| | conn1 | conn2 | conn3 |
|---------|--------------------|--------------------|-------------------|
| action1 | :broken_heart: 403 | :green_heart: 200 | :green_heart: 200 |
| action2 | :green_heart: 200 | :green_heart: 200 | :green_heart: 200 |
| action3 | :broken_heart: 403 | :broken_heart: 403 | :green_heart: 200 |
| action4 | :broken_heart: 403 | :broken_heart: 403 | :green_heart: 200 |