Current section
Files
Jump to
Current section
Files
lib/cassette/plug/require_role_plug.ex
defmodule Cassette.Plug.RequireRolePlug do
@moduledoc """
Plug to check presence of user roles
"""
@behaviour Plug
import Plug.Conn
alias Cassette.User
alias Plug.Conn
@type role_param :: String.t() | [String.t()] | (Conn.t() -> String.t())
def init(opts) do
_role = Keyword.fetch!(opts, :role)
opts
end
@doc """
Fetches the current user from the session.
Returns `nil` if has no user
"""
@spec current_user(Conn.t()) :: User.t() | nil
def current_user(conn) do
conn |> fetch_session |> get_session("cas_user")
end
defp __config__(opts) do
apply(opts[:cassette] || Cassette, :config, [])
end
defdelegate has_role?(conn, roles, opts), to: __MODULE__, as: :role?
@doc """
Tests if the user has roles.
When roles is a list, tests if `current_user` has *any* of the roles.
When roles ia function, it will receive the `Plug.Conn.t` and must return role to validate.
Returns `false` if there is no user in the session.
"""
@spec role?(Conn.t(), role_param, Keyword.t()) :: boolean
def role?(conn, roles, opts) when is_list(roles) do
user = current_user(conn)
Enum.any?(roles, &User.has_role?(user, __config__(opts), &1))
end
def role?(conn, role_fn, opts) when is_function(role_fn, 1) do
role?(conn, role_fn.(conn), opts)
end
def role?(conn, role, opts) do
role?(conn, [role], opts)
end
defdelegate has_raw_role?(conn, roles), to: __MODULE__, as: :raw_role?
@doc """
Tests if the user has (raw) roles.
Arguments follow the same logic as role?/2 but they are forwarded to `Cassette.User.has_raw_role?/2`
"""
@spec raw_role?(Conn.t(), role_param) :: boolean
def raw_role?(conn, roles) when is_list(roles) do
user = current_user(conn)
Enum.any?(roles, &User.has_raw_role?(user, &1))
end
def raw_role?(conn, role_fn) when is_function(role_fn, 1) do
raw_role?(conn, role_fn.(conn))
end
def raw_role?(conn, role) do
raw_role?(conn, [role])
end
def call(conn, opts) do
role = Keyword.fetch!(opts, :role)
if role?(conn, role, opts) do
conn
else
(opts[:on_forbidden] ||
fn c ->
c |> resp(403, "Forbidden") |> halt
end).(conn)
end
end
end