From 866d806b4786dffc923286afbb440eaa44b1d4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Bergstr=C3=B6m?= Date: Wed, 24 Jul 2024 22:21:24 +0200 Subject: [PATCH] improvement: compile-time check to make sure that the configured `token_resource` is an Ash.Resource (#749) * improvement(Tokens): improved compile-time validation of the token_resource option of the tokens DSL by checking that the passed value is an Ash.Resource. * improvement(Tokens): removed unnecessary stuff from the test file. * improvement(Tokens): fixed credo warning and changed some things after PR feedback --- lib/ash_authentication/jwt.ex | 6 +- lib/ash_authentication/verifier.ex | 11 +++- .../user_with_bad_token_test.exs | 57 +++++++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 test/ash_authentication/user_with_bad_token_test.exs diff --git a/lib/ash_authentication/jwt.ex b/lib/ash_authentication/jwt.ex index 4b5ef22..b974251 100644 --- a/lib/ash_authentication/jwt.ex +++ b/lib/ash_authentication/jwt.ex @@ -54,6 +54,8 @@ defmodule AshAuthentication.Jwt do specified in integer positive hours. """ + require Logger + alias Ash.Resource alias AshAuthentication.{Info, Jwt.Config, TokenResource} @@ -113,7 +115,9 @@ defmodule AshAuthentication.Jwt do :ok <- maybe_store_token(token, resource, user, purpose, action_opts) do {:ok, token, claims} else - {:error, _reason} -> :error + {:error, reason} -> + Logger.error("Failed to generate token for user: #{inspect reason, pretty: true}") + :error end end diff --git a/lib/ash_authentication/verifier.ex b/lib/ash_authentication/verifier.ex index 82fb8f8..3efa8e5 100644 --- a/lib/ash_authentication/verifier.ex +++ b/lib/ash_authentication/verifier.ex @@ -109,11 +109,16 @@ defmodule AshAuthentication.Verifier do end end + @spec token_resource?(any()) :: {boolean(), any()} + defp token_resource?(module) do + {Spark.Dsl.is?(module, Ash.Resource), module} + end + defp validate_token_resource(dsl_state) do if_tokens_enabled(dsl_state, fn dsl_state -> with {:ok, resource} when is_truthy(resource) <- Info.authentication_tokens_token_resource(dsl_state), - true <- is_atom(resource) do + {true, _resource} <- token_resource?(resource) do :ok else {:ok, falsy} when is_falsy(falsy) -> @@ -122,11 +127,11 @@ defmodule AshAuthentication.Verifier do {:error, reason} -> {:error, reason} - false -> + {false, bad_token_resource} -> {:error, DslError.exception( path: [:authentication, :tokens, :token_resource], - message: "is not a valid module name" + message: "`#{inspect(bad_token_resource)}` is not a valid token resource module name" )} end end) diff --git a/test/ash_authentication/user_with_bad_token_test.exs b/test/ash_authentication/user_with_bad_token_test.exs new file mode 100644 index 0000000..c1ad61a --- /dev/null +++ b/test/ash_authentication/user_with_bad_token_test.exs @@ -0,0 +1,57 @@ +defmodule AshAuthentication.UserWithBadTokenTest do + @moduledoc false + + use DataCase, async: true + + test "cannot compile with bad token_resource configured" do + assert_raise Spark.Error.DslError, ~r/`BadToken` is not a valid token resource module name/, fn -> + defmodule UserWithBadToken do + @moduledoc false + use Ash.Resource, + data_layer: AshPostgres.DataLayer, + extensions: [AshAuthentication], + validate_domain_inclusion?: false, + domain: Example + + attributes do + uuid_primary_key :id, writable?: true + attribute :email, :ci_string, allow_nil?: false, public?: true + attribute :hashed_password, :string, allow_nil?: true, sensitive?: true, public?: false + create_timestamp :created_at + update_timestamp :updated_at + end + + authentication do + tokens do + enabled? true + token_resource BadToken + signing_secret fn _, _ -> :dummy end + end + + strategies do + password do + identity_field :email + + resettable do + sender fn _user, _token, _opts -> :noop end + end + end + end + end + + actions do + defaults [:create, :read, :update, :destroy] + end + + identities do + identity :email, [:email], eager_check_with: Example + end + + postgres do + table "user_with_bad_token_required" + repo Example.Repo + end + end + end + end +end