Cap is Central Authentication Plug for Phoenix, access control library with Role-based access control (RBAC) and Attribute-based access control (ABAC)
Add the package as a dependency in your Elixir project using something along the lines of:
def deps do
[
{:cap, "~> 0.2"}
]
end
In your configuration you can set values for RBAC directly, eg
config :cap,
effect: :deny,
#Allow Root to any
exception: :root,
policy: %{
nil: %{
# Deny Anonymous use to any action in any Controller
:* => :*
},
admin: %{
# Deny Admin use to any action in UserController
ThetaWeb.UserController => :*
},
mod: %{
# Deny Mod use to action :update and :delete in ArticleController
ThetaWeb.CMS.ArticleController => [:update, :delete],
# Deny Mod use to any action in UserController
ThetaWeb.UserController => :*
},
user: %{
# Deny User use to any action in any Controller
:* => :*
},
},
# secret_key for encrypt, decrypt value in session
secret_key: "h25R39fifOx449BCv084I9ksl/WA3Xi5"
Add plug to scope in pipeline
pipeline :admin do
plug :put_layout, {ThetaWeb.LayoutView, "layout_admin.html"}
plug Cap
end
Apply pipeline in scope
scope "/admin", ThetaWeb do
pipe_through [:browser, :admin]
resources "/users", UserController
resources "/article", CMS.ArticleController
end
Add resource for session
defmodule ThetaWeb.SessionController do
use ThetaWeb, :controller
import Cap
alias Theta.Account
...
def create(
conn,
%{
"user" => %{
"email" => email,
"password" => password
}
}
) do
case Account.authenticate_by_email_password(email, password) do
{:ok, user} ->
conn
|> put_flash(:info, "Welcome #{user.name}!")
|> Cap.sign_in(user.id, user.role) # Add id and role into session
|> redirect(to: "/user/me")
{:error, :unauthorized} ->
conn
|> put_flash(:error, "Bad email/password combination")
|> redirect(to: Routes.session_path(conn, :new))
end
end
def delete(conn, _) do
conn
|> configure_session(drop: true)
|> redirect(to: "/")
end
end
Apply ABAC in for controller:
defmodule ThetaWeb.CMS.ArticleController do
use ThetaWeb, :controller
alias Theta.CMS
...
# id in param of func :show, :edit, :update, :delete
def abac(id) do
article = CMS.get_article!(id)
article.author_id # compare with id in Cap sign_in
end
end
Implement Plug.Exception for Cap.ErrorHandler
defimpl Plug.Exception, for: Cap.ErrorHandler do
def status(exception) do
case Integer.parse(exception.message) do
:error -> 404
{int, _} -> 403
end
end
end