From a45133823e505f565a2d6b495d27b607127ecc78 Mon Sep 17 00:00:00 2001 From: Jinkyou Son Date: Wed, 27 Nov 2024 05:58:45 +0900 Subject: [PATCH] improvement: Add opts to retrieve funs of AshAuthentication.Plug.Helpers (#847) --- lib/ash_authentication/plug/helpers.ex | 36 +++++++++----- lib/ash_authentication/plug/macros.ex | 12 ++--- test/ash_authentication/plug/helpers_test.exs | 48 ++++++++++++++++++- test/ash_authentication/plug_test.exs | 18 +++---- test/support/example/user.ex | 4 ++ .../example/user_with_token_required.ex | 4 ++ 6 files changed, 94 insertions(+), 28 deletions(-) diff --git a/lib/ash_authentication/plug/helpers.ex b/lib/ash_authentication/plug/helpers.ex index d498ebbb..dffabf62 100644 --- a/lib/ash_authentication/plug/helpers.ex +++ b/lib/ash_authentication/plug/helpers.ex @@ -63,8 +63,8 @@ defmodule AshAuthentication.Plug.Helpers do If there is no user present for a resource then the assign is set to `nil`. """ - @spec retrieve_from_session(Conn.t(), module) :: Conn.t() - def retrieve_from_session(conn, otp_app) do + @spec retrieve_from_session(Conn.t(), module, keyword) :: Conn.t() + def retrieve_from_session(conn, otp_app, opts \\ []) do otp_app |> AshAuthentication.authenticated_resources() |> Stream.map( @@ -91,9 +91,14 @@ defmodule AshAuthentication.Plug.Helpers do context: Ash.PlugHelpers.get_context(conn) || %{} ), {:ok, user} <- - AshAuthentication.subject_to_user(subject, resource, - tenant: Ash.PlugHelpers.get_tenant(conn), - context: Ash.PlugHelpers.get_context(conn) || %{} + AshAuthentication.subject_to_user( + subject, + resource, + [ + tenant: Ash.PlugHelpers.get_tenant(conn), + context: Ash.PlugHelpers.get_context(conn) || %{} + ] + |> Keyword.merge(opts) ) do Conn.assign(conn, current_subject_name, user) else @@ -105,8 +110,10 @@ defmodule AshAuthentication.Plug.Helpers do with subject when is_binary(subject) <- Conn.get_session(conn, options.subject_name), {:ok, user} <- - AshAuthentication.subject_to_user(subject, resource, - tenant: Ash.PlugHelpers.get_tenant(conn) + AshAuthentication.subject_to_user( + subject, + resource, + [tenant: Ash.PlugHelpers.get_tenant(conn)] |> Keyword.merge(opts) ) do Conn.assign(conn, current_subject_name, user) else @@ -128,8 +135,8 @@ defmodule AshAuthentication.Plug.Helpers do If there is no user present for a resource then the assign is set to `nil`. """ - @spec retrieve_from_bearer(Conn.t(), module) :: Conn.t() - def retrieve_from_bearer(conn, otp_app) do + @spec retrieve_from_bearer(Conn.t(), module, keyword) :: Conn.t() + def retrieve_from_bearer(conn, otp_app, opts \\ []) do conn |> Conn.get_req_header("authorization") |> Stream.filter(&String.starts_with?(&1, "Bearer ")) @@ -140,9 +147,14 @@ defmodule AshAuthentication.Plug.Helpers do {:ok, token_record} <- validate_token(resource, jti), {:ok, user} <- - AshAuthentication.subject_to_user(subject, resource, - tenant: Ash.PlugHelpers.get_tenant(conn), - context: Ash.PlugHelpers.get_context(conn) || %{} + AshAuthentication.subject_to_user( + subject, + resource, + [ + tenant: Ash.PlugHelpers.get_tenant(conn), + context: Ash.PlugHelpers.get_context(conn) || %{} + ] + |> Keyword.merge(opts) ), {:ok, subject_name} <- Info.authentication_subject_name(resource), current_subject_name <- current_subject_name(subject_name) do diff --git a/lib/ash_authentication/plug/macros.ex b/lib/ash_authentication/plug/macros.ex index 01013e30..68734383 100644 --- a/lib/ash_authentication/plug/macros.ex +++ b/lib/ash_authentication/plug/macros.ex @@ -60,12 +60,12 @@ defmodule AshAuthentication.Plug.Macros do @doc """ Attempt to retrieve all users from the connections' session. - A wrapper around `AshAuthentication.Plug.Helpers.retrieve_from_session/2` + A wrapper around `AshAuthentication.Plug.Helpers.retrieve_from_session/3` with the `otp_app` already present. """ @spec load_from_session(Conn.t(), any) :: Conn.t() - def load_from_session(conn, _opts), - do: Helpers.retrieve_from_session(conn, unquote(otp_app)) + def load_from_session(conn, opts), + do: Helpers.retrieve_from_session(conn, unquote(otp_app), opts) end end @@ -78,11 +78,11 @@ defmodule AshAuthentication.Plug.Macros do @doc """ Attempt to retrieve users from the `Authorization` header(s). - A wrapper around `AshAuthentication.Plug.Helpers.retrieve_from_bearer/2` with the `otp_app` already present. + A wrapper around `AshAuthentication.Plug.Helpers.retrieve_from_bearer/3` with the `otp_app` already present. """ @spec load_from_bearer(Conn.t(), any) :: Conn.t() - def load_from_bearer(conn, _opts), - do: Helpers.retrieve_from_bearer(conn, unquote(otp_app)) + def load_from_bearer(conn, opts), + do: Helpers.retrieve_from_bearer(conn, unquote(otp_app), opts) end end diff --git a/test/ash_authentication/plug/helpers_test.exs b/test/ash_authentication/plug/helpers_test.exs index a2c27737..da439069 100644 --- a/test/ash_authentication/plug/helpers_test.exs +++ b/test/ash_authentication/plug/helpers_test.exs @@ -49,7 +49,7 @@ defmodule AshAuthentication.Plug.HelpersTest do end end - describe "retrieve_from_session/2" do + describe "retrieve_from_session/3" do test "when token presence is not required it loads any subjects stored in the session", %{ conn: conn } do @@ -120,9 +120,32 @@ defmodule AshAuthentication.Plug.HelpersTest do refute conn.assigns.current_user_with_token_required end + + test "with opts", %{conn: conn} do + # without token + user = build_user() + subject = AshAuthentication.user_to_subject(user) + + conn0 = + conn + |> Conn.put_session("user", subject) + |> Helpers.retrieve_from_session(:ash_authentication, load: [:dummy_calc]) + + assert conn0.assigns.current_user.dummy_calc == "dummy" + + # with token + user_with_token = build_user_with_token_required() + + conn1 = + conn + |> Conn.put_session("user_with_token_required_token", user_with_token.__metadata__.token) + |> Helpers.retrieve_from_session(:ash_authentication, load: [:dummy_calc]) + + assert conn1.assigns.current_user_with_token_required.dummy_calc == "dummy" + end end - describe "retrieve_from_bearer/2" do + describe "retrieve_from_bearer/3" do test "when token presense is not required it loads any subjects from authorization header(s)", %{conn: conn} do user = build_user() @@ -200,6 +223,27 @@ defmodule AshAuthentication.Plug.HelpersTest do refute is_map_key(conn.assigns, :current_user_with_token_required) end + + test "with opts", %{conn: conn} do + user = build_user() + + conn0 = + conn + |> Conn.put_req_header("authorization", "Bearer #{user.__metadata__.token}") + |> Helpers.retrieve_from_bearer(:ash_authentication, load: [:dummy_calc]) + + assert conn0.assigns.current_user.dummy_calc == "dummy" + + # with token + user_with_token = build_user_with_token_required() + + conn1 = + conn + |> Conn.put_req_header("authorization", "Bearer #{user_with_token.__metadata__.token}") + |> Helpers.retrieve_from_bearer(:ash_authentication, load: [:dummy_calc]) + + assert conn1.assigns.current_user_with_token_required.dummy_calc == "dummy" + end end describe "revoke_bearer_tokens/2" do diff --git a/test/ash_authentication/plug_test.exs b/test/ash_authentication/plug_test.exs index 6e3cb327..d03524c3 100644 --- a/test/ash_authentication/plug_test.exs +++ b/test/ash_authentication/plug_test.exs @@ -56,33 +56,35 @@ defmodule AshAuthentication.PlugTest do end end - describe "load_from_session/2" do - test "it delegates to Helpers.retrieve_from_session/2" do + describe "load_from_session/3" do + test "it delegates to Helpers.retrieve_from_session/3" do conn = conn(:get, "/", %{}) Helpers - |> expect(:retrieve_from_session, fn rx_conn, otp_app -> + |> expect(:retrieve_from_session, fn rx_conn, otp_app, opts -> assert otp_app == :ash_authentication assert conn == rx_conn + assert opts == [load: [:company]] end) conn - |> AuthPlug.load_from_session([]) + |> AuthPlug.load_from_session(load: [:company]) end end - describe "load_from_bearer/2" do - test "it delegates to Helpers.retrieve_from_bearer/2" do + describe "load_from_bearer/3" do + test "it delegates to Helpers.retrieve_from_bearer/3" do conn = conn(:get, "/", %{}) Helpers - |> expect(:retrieve_from_bearer, fn rx_conn, otp_app -> + |> expect(:retrieve_from_bearer, fn rx_conn, otp_app, opts -> assert otp_app == :ash_authentication assert conn == rx_conn + assert opts == [load: [:company]] end) conn - |> AuthPlug.load_from_bearer([]) + |> AuthPlug.load_from_bearer(load: [:company]) end end diff --git a/test/support/example/user.ex b/test/support/example/user.ex index 958ddcd1..bf46b346 100644 --- a/test/support/example/user.ex +++ b/test/support/example/user.ex @@ -126,6 +126,10 @@ defmodule Example.User do end end + calculations do + calculate :dummy_calc, :string, expr("dummy") + end + code_interface do define :update_user, action: :update end diff --git a/test/support/example/user_with_token_required.ex b/test/support/example/user_with_token_required.ex index 0ded00b4..47a2d29b 100644 --- a/test/support/example/user_with_token_required.ex +++ b/test/support/example/user_with_token_required.ex @@ -51,6 +51,10 @@ defmodule Example.UserWithTokenRequired do defaults [:create, :read, :update, :destroy] end + calculations do + calculate :dummy_calc, :string, expr("dummy") + end + identities do identity :email, [:email] end