lib/glific_web/schema/middleware/authorize.ex

defmodule GlificWeb.Schema.Middleware.Authorize do
  @moduledoc """
  Implementing middleware functions to transform errors from Ecto Changeset into a format
  consumable and displayable to the API user. This version is specifically for mutations.
  """
  import GlificWeb.Gettext

  @behaviour Absinthe.Middleware

  @doc """
  This is the main middleware callback.

  It receives an %Absinthe.Resolution{} struct and it needs to return an %Absinthe.Resolution{} struct.
  The second argument will be whatever value was passed to the middleware call that setup the middleware.
  """
  @spec call(Absinthe.Resolution.t(), term()) :: Absinthe.Resolution.t()
  def call(resolution, role) do
    with %{roles: roles} <- resolution.context.current_user,
         true <- is_valid_role?(roles, role) do
      resolution
    else
      _ ->
        resolution
        |> Absinthe.Resolution.put_result({:error, dgettext("errors", "Unauthorized")})
    end
  end

  # Check role with hierarchy
  @spec is_valid_role?(list(), atom() | list()) :: boolean()
  defp is_valid_role?(_, :any), do: true
  defp is_valid_role?(roles, :glific_admin), do: is_valid_role?(roles, [:glific_admin])
  defp is_valid_role?(roles, :admin), do: is_valid_role?(roles, [:glific_admin, :admin])

  defp is_valid_role?(roles, :manager),
    do: is_valid_role?(roles, [:glific_admin, :admin, :manager])

  defp is_valid_role?(roles, :staff),
    do: is_valid_role?(roles, [:glific_admin, :admin, :manager, :staff])

  defp is_valid_role?(roles, role) when is_list(role), do: Enum.any?(roles, fn x -> x in role end)
  defp is_valid_role?(_, _), do: false
end