defmodule Bones73kWeb.EnsureRolePlug do @moduledoc """ This plug ensures that a user has a particular role before accessing a given route. ## Example Let's suppose we have three roles: :admin, :manager and :user. If you want a user to have at least manager role, so admins and managers are authorised to access a given route plug Bones73kWeb.EnsureRolePlug, [:admin, :manager] If you want to give access only to an admin: plug Bones73kWeb.EnsureRolePlug, :admin """ import Plug.Conn alias Bones73k.Accounts alias Bones73k.Accounts.User alias Phoenix.Controller alias Plug.Conn @doc false @spec init(any()) :: any() def init(config), do: config @doc false @spec call(Conn.t(), atom() | [atom()]) :: Conn.t() def call(conn, roles) do user_token = get_session(conn, :user_token) (user_token && Accounts.get_user_by_session_token(user_token)) |> has_role?(roles) |> maybe_halt(conn) end defp has_role?(%User{} = user, roles) when is_list(roles), do: Enum.any?(roles, &has_role?(user, &1)) defp has_role?(%User{role: role}, role), do: true defp has_role?(_user, _role), do: false defp maybe_halt(true, conn), do: conn defp maybe_halt(_any, conn) do conn |> Controller.put_flash(:error, "Unauthorised") |> Controller.redirect(to: signed_in_path(conn)) |> halt() end defp signed_in_path(_conn), do: "/" end