-
Notifications
You must be signed in to change notification settings - Fork 30
Authorization
This page looks at how to handle access control using the output from Openmaize.Authenticate.
The first section deals with creating a Plug which you can add to the controller, and the second section looks at overriding the default action
function (this method can only be used with Phoenix apps).
The functions user_check
, id_check
, auth_error
and auth_action
in the examples below are all defined in this module.
The most basic check is the user_check
function, which just checks if the current user is not nil.
To use this function, add the following line to your controller:
plug :user_check
the above line calls the user_check function before every route in the controller. You can also specify which routes you want it run before:
plug :user_check when action in [:index, :show]
or which routes you do not want it run before:
plug :user_check when not action in [:new, :create]
The id_check
function checks that the id of the current user is the same as the id in the route. It can be used in the same way as user_check
:
plug :id_check when action in [:edit, :update, :delete]
The following Plug can be used to grant / deny access based on user role:
def role_check(%Plug.Conn{assigns: %{current_user: nil}} = conn, _opts) do
auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def role_check(%Plug.Conn{assigns: %{current_user: current_user}} = conn, opts) do
if opts[:roles] && current_user.role in opts[:roles], do: conn,
else: auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end
To call this Plug, add the following line to the controller:
plug :role_check, [roles: ["admin", "user"]] when action in [:create, :update]
The following Plug shows how you can combine id_check
and role_check
:
def id_or_admin(%Plug.Conn{assigns: %{current_user: nil}} = conn, _opts) do
auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def id_or_admin(%Plug.Conn{params: %{"id" => id}, assigns: %{current_user:
%{id: current_id}}} = conn, _opts) do
if id == to_string(current_id) or current_user.role == "admin", do: conn,
else: auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end
In this example, after the check for user id, there is an additional check to see if the current_user is an admin. In other words, this resource is accessable to a normal user if it is his / her own page and any admin.
In Phoenix apps, the function action/2
is called before each route. We can override this and authorize the user at this stage. This will affect every route in the controller.
In all the examples below, we will add the current user to the output. As a result, you will need to add user
to the argument list in each function. For example, def index(conn, params)
will become def index(conn, params, user)
.
The auth_action
function in the example Authorize module just checks for a current user. It then either sends the arguments conn, params, current_user
to the route or denies access to the user.
To use this function, add the following line to the controller:
def action(conn, _), do: auth_action(conn, __MODULE__)
In this case, the action
function is very similar to the id_check
function:
def auth_id(%Plug.Conn{assigns: %{current_user: nil}} = conn, _) do
auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def auth_id(%Plug.Conn{assigns: %{current_user: %{id: current_id}},
params: %{"id" => id} = params} = conn, module) do
if id == to_string(current_id) do
apply(module, action_name(conn), [conn, params, current_user])
else
auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end
end
Call the function in the same way, by adding the following line to the controller:
def action(conn, _), do: auth_id(conn, __MODULE__)
The following action
function can be used to grant / deny access based on user role:
def auth_role(%Plug.Conn{assigns: %{current_user: nil}} = conn, _, _) do
auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def auth_role(%Plug.Conn{assigns: %{current_user: current_user}} = conn, roles, module) do
if current_user.role in roles do
apply(module, action_name(conn), [conn, conn.params, current_user])
else
auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end
end
In this case, call the function with the list of permitted roles as well as the module name:
def action(conn, _), do: auth_role(conn, ["admin", "user"], __MODULE__)