From 479325bff8e5a9cf11c9696cc2adebab2ec4908f Mon Sep 17 00:00:00 2001 From: RicoPleasure Date: Tue, 8 Jul 2025 18:06:43 +0100 Subject: [PATCH 1/6] feat: add plug structure --- .../plugs/authorization_user_types.ex | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 lib/atlas_web/plugs/authorization_user_types.ex diff --git a/lib/atlas_web/plugs/authorization_user_types.ex b/lib/atlas_web/plugs/authorization_user_types.ex new file mode 100644 index 0000000..767fda1 --- /dev/null +++ b/lib/atlas_web/plugs/authorization_user_types.ex @@ -0,0 +1,52 @@ +defmodule AtlasWeb.Plugs.AuthorizationUserTypes do + + def init(args) do + cond do + user_types = args[:user_types] -> + %{user_types: user_types, on_failure: args[:on_failure] || :redirect} + user_type = args[:user_type] -> + %{user_types: [:user_type], on_failure: args[:on_failure] || :redirect} + true -> + raise ArgumentError, "Must provide either :user_type or :user_types option" + end + + def call(conn, %{user_types: allowed_types, on_failure: on_failure}) do + current_user = conn.assigns[:current_user] + + cond do + is_nil(current_user) -> + handle_unauthorized(conn, on_failure, :not_authenticated) + + is_nil(Map.get(current_user, :user_type)) -> + handle_unauthorized(conn, on_failure, :no_user_type) + + current_user.user_type not in allowed_types -> + handle_unauthorized(conn, on_failure, :insufficient_permissions) + + true -> + conn + end + + defp handle_unauthorized(conn, :redirect, reason) do + conn + |> redirect(to: "/") + |> halt() + end + + defp handle_unauthorized(conn, :json, reason) do + conn + |> json(%{error: unauthorized_message(reason)}) + |> halt() + end + + defp handle_unauthorized(conn, :halt, reason) do + conn + |> put_status(:forbidden) + |> halt() + end + + defp unauthorized_message(:not_authenticated), do: "You must be logged in" + defp unauthorized_message(:no_user_type), do: "User type not defined" + defp unauthorized_message(:insufficient_permissions), do: "You don't have enough permission" + +end From c2bea8a4c81b331074bd00ec673e930b8fb6e14d Mon Sep 17 00:00:00 2001 From: RicoPleasure Date: Tue, 8 Jul 2025 18:08:22 +0100 Subject: [PATCH 2/6] fix: argument --- lib/atlas_web/plugs/authorization_user_types.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/atlas_web/plugs/authorization_user_types.ex b/lib/atlas_web/plugs/authorization_user_types.ex index 767fda1..f18b23b 100644 --- a/lib/atlas_web/plugs/authorization_user_types.ex +++ b/lib/atlas_web/plugs/authorization_user_types.ex @@ -5,7 +5,7 @@ defmodule AtlasWeb.Plugs.AuthorizationUserTypes do user_types = args[:user_types] -> %{user_types: user_types, on_failure: args[:on_failure] || :redirect} user_type = args[:user_type] -> - %{user_types: [:user_type], on_failure: args[:on_failure] || :redirect} + %{user_types: [user_type], on_failure: args[:on_failure] || :redirect} true -> raise ArgumentError, "Must provide either :user_type or :user_types option" end From 1875bf10992737db05a66734899ad202a1398749 Mon Sep 17 00:00:00 2001 From: RicoPleasure Date: Tue, 8 Jul 2025 18:16:39 +0100 Subject: [PATCH 3/6] fix: errors --- .../plugs/authorization_user_types.ex | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/atlas_web/plugs/authorization_user_types.ex b/lib/atlas_web/plugs/authorization_user_types.ex index f18b23b..6936e1b 100644 --- a/lib/atlas_web/plugs/authorization_user_types.ex +++ b/lib/atlas_web/plugs/authorization_user_types.ex @@ -1,13 +1,18 @@ defmodule AtlasWeb.Plugs.AuthorizationUserTypes do + import Plug.Conn + import Phoenix.Controller def init(args) do cond do user_types = args[:user_types] -> %{user_types: user_types, on_failure: args[:on_failure] || :redirect} + user_type = args[:user_type] -> %{user_types: [user_type], on_failure: args[:on_failure] || :redirect} + true -> raise ArgumentError, "Must provide either :user_type or :user_types option" + end end def call(conn, %{user_types: allowed_types, on_failure: on_failure}) do @@ -25,28 +30,28 @@ defmodule AtlasWeb.Plugs.AuthorizationUserTypes do true -> conn + end end - defp handle_unauthorized(conn, :redirect, reason) do + defp handle_unauthorized(conn, :redirect, _reason) do conn - |> redirect(to: "/") - |> halt() + |> redirect(to: "/") + |> halt() end defp handle_unauthorized(conn, :json, reason) do conn - |> json(%{error: unauthorized_message(reason)}) - |> halt() + |> json(%{error: unauthorized_message(reason)}) + |> halt() end - defp handle_unauthorized(conn, :halt, reason) do + defp handle_unauthorized(conn, :halt, _reason) do conn - |> put_status(:forbidden) - |> halt() + |> put_status(:forbidden) + |> halt() end defp unauthorized_message(:not_authenticated), do: "You must be logged in" defp unauthorized_message(:no_user_type), do: "User type not defined" defp unauthorized_message(:insufficient_permissions), do: "You don't have enough permission" - end From 45730b62c70b369e33d1cbce52717d33feef2352 Mon Sep 17 00:00:00 2001 From: RicoPleasure Date: Tue, 8 Jul 2025 18:18:30 +0100 Subject: [PATCH 4/6] chore: add moduledoc --- lib/atlas_web/plugs/authorization_user_types.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/atlas_web/plugs/authorization_user_types.ex b/lib/atlas_web/plugs/authorization_user_types.ex index 6936e1b..6736c33 100644 --- a/lib/atlas_web/plugs/authorization_user_types.ex +++ b/lib/atlas_web/plugs/authorization_user_types.ex @@ -2,6 +2,17 @@ defmodule AtlasWeb.Plugs.AuthorizationUserTypes do import Plug.Conn import Phoenix.Controller + @moduledoc """ + A plug that restricts route access based on user types. + + Usage: + # In your router or controller + plug MyAppWeb.Plugs.RequireUserType, user_types: [:admin, :moderator] + + # Or for a single user type + plug MyAppWeb.Plugs.RequireUserType, user_type: :admin + """ + def init(args) do cond do user_types = args[:user_types] -> From d8fc16cc5c7df71ac4b33774f57359ebdc7c1c3c Mon Sep 17 00:00:00 2001 From: RicoPleasure Date: Tue, 8 Jul 2025 18:20:14 +0100 Subject: [PATCH 5/6] format --- lib/atlas_web/plugs/authorization_user_types.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/atlas_web/plugs/authorization_user_types.ex b/lib/atlas_web/plugs/authorization_user_types.ex index 6736c33..9872528 100644 --- a/lib/atlas_web/plugs/authorization_user_types.ex +++ b/lib/atlas_web/plugs/authorization_user_types.ex @@ -2,7 +2,7 @@ defmodule AtlasWeb.Plugs.AuthorizationUserTypes do import Plug.Conn import Phoenix.Controller - @moduledoc """ + @moduledoc """ A plug that restricts route access based on user types. Usage: From a7bc1db10859fb8299d1b1855e016c6c4c3a25db Mon Sep 17 00:00:00 2001 From: RicoPleasure Date: Mon, 14 Jul 2025 20:27:34 +0100 Subject: [PATCH 6/6] feat: add plug --- .../plugs/authorization_user_types.ex | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/lib/atlas_web/plugs/authorization_user_types.ex b/lib/atlas_web/plugs/authorization_user_types.ex index 9872528..bc934f4 100644 --- a/lib/atlas_web/plugs/authorization_user_types.ex +++ b/lib/atlas_web/plugs/authorization_user_types.ex @@ -1,68 +1,59 @@ -defmodule AtlasWeb.Plugs.AuthorizationUserTypes do +defmodule AtlasWeb.Plugs.UserRequires do import Plug.Conn import Phoenix.Controller + alias Atlas.Accounts.Guardian @moduledoc """ A plug that restricts route access based on user types. Usage: # In your router or controller - plug MyAppWeb.Plugs.RequireUserType, user_types: [:admin, :moderator] + plug MyAppWeb.Plugs.UserRequires, user_types: [:admin, :moderator] # Or for a single user type - plug MyAppWeb.Plugs.RequireUserType, user_type: :admin + plug MyAppWeb.Plugs.UserRequires, user_type: :admin """ def init(args) do cond do user_types = args[:user_types] -> - %{user_types: user_types, on_failure: args[:on_failure] || :redirect} + %{user_types: user_types} user_type = args[:user_type] -> - %{user_types: [user_type], on_failure: args[:on_failure] || :redirect} + %{user_types: [user_type]} true -> raise ArgumentError, "Must provide either :user_type or :user_types option" end end - def call(conn, %{user_types: allowed_types, on_failure: on_failure}) do - current_user = conn.assigns[:current_user] - - cond do - is_nil(current_user) -> - handle_unauthorized(conn, on_failure, :not_authenticated) - - is_nil(Map.get(current_user, :user_type)) -> - handle_unauthorized(conn, on_failure, :no_user_type) - - current_user.user_type not in allowed_types -> - handle_unauthorized(conn, on_failure, :insufficient_permissions) + def call(conn, %{user_types: allowed_types}) do + {user, _session} = Guardian.Plug.current_resource(conn) + user_type = user.type + case user_type_allowed?(user_type, allowed_types) do true -> conn + + false -> + handle_unauthorized(conn, user, allowed_types) end - end - defp handle_unauthorized(conn, :redirect, _reason) do conn - |> redirect(to: "/") - |> halt() end - defp handle_unauthorized(conn, :json, reason) do - conn - |> json(%{error: unauthorized_message(reason)}) - |> halt() + def user_type_allowed?(user_type, allowed_types) do + user_type in allowed_types end - defp handle_unauthorized(conn, :halt, _reason) do + def handle_unauthorized(conn, user, allowed_types) do conn |> put_status(:forbidden) + |> json(%{ + error: "Unauthorized access", + user_type: user.type, + allowed_types: allowed_types + }) |> halt() end - - defp unauthorized_message(:not_authenticated), do: "You must be logged in" - defp unauthorized_message(:no_user_type), do: "User type not defined" - defp unauthorized_message(:insufficient_permissions), do: "You don't have enough permission" end