From 37985ac1cfcfbce986d0b2dcb2e9ce5a8bab6faa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Gilberto=20Balsini=20Moura?=
 <joaobalsini@Joaos-MacBook-Pro-2.local>
Date: Mon, 14 Sep 2020 12:08:38 -0300
Subject: [PATCH] Add authorisation plug

---
 lib/real_estate_web/plugs/ensure_role_plug.ex | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 lib/real_estate_web/plugs/ensure_role_plug.ex

diff --git a/lib/real_estate_web/plugs/ensure_role_plug.ex b/lib/real_estate_web/plugs/ensure_role_plug.ex
new file mode 100644
index 0000000..0543909
--- /dev/null
+++ b/lib/real_estate_web/plugs/ensure_role_plug.ex
@@ -0,0 +1,52 @@
+defmodule RealEstateWeb.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 RealEstateWeb.EnsureRolePlug, [:admin, :manager]
+
+  If you want to give access only to an admin:
+  plug RealEstateWeb.EnsureRolePlug, :admin
+  """
+  import Plug.Conn
+
+  alias RealEstate.Accounts
+  alias RealEstate.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