From aa638e02303beec3e4ea87508dacb6d37dc17569 Mon Sep 17 00:00:00 2001 From: James Harton Date: Wed, 26 Oct 2022 12:52:50 +1300 Subject: [PATCH] improvement: move subject_name uniqueness validation to compile time. --- lib/ash_authentication/plug.ex | 42 +++++++++++++++++++++++++-- lib/ash_authentication/validations.ex | 22 -------------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/lib/ash_authentication/plug.ex b/lib/ash_authentication/plug.ex index e53df79..6d56efd 100644 --- a/lib/ash_authentication/plug.ex +++ b/lib/ash_authentication/plug.ex @@ -69,9 +69,10 @@ defmodule AshAuthentication.Plug do do useful things like session and query param fetching. """ - alias Ash.{Changeset, Resource} + alias Ash.{Api, Changeset, Resource} alias AshAuthentication.Plug.Helpers alias Plug.Conn + alias Spark.Dsl.Extension @type authenticator_config :: %{ api: module, @@ -110,9 +111,44 @@ defmodule AshAuthentication.Plug do |> Keyword.fetch!(:otp_app) |> Macro.expand_once(__CALLER__) - AshAuthentication.Validations.validate_unique_subject_names(otp_app) - quote do + require Ash.Api.Info + + unquote(otp_app) + |> Application.compile_env(:ash_apis, []) + |> Stream.flat_map(&Api.Info.depend_on_resources(&1)) + |> Stream.map(&{&1, Extension.get_persisted(&1, :authentication)}) + |> Stream.reject(&(elem(&1, 1) == nil)) + |> Stream.map(&{elem(&1, 0), elem(&1, 1).subject_name}) + |> Enum.group_by(&elem(&1, 1), &elem(&1, 0)) + |> Enum.reject(&(length(elem(&1, 1)) < 2)) + |> case do + [] -> + nil + + duplicates -> + import AshAuthentication.Utils, only: [to_sentence: 2] + + duplicates = + duplicates + |> Enum.map(fn {subject_name, resources} -> + resources = + resources + |> Enum.map(&"`#{inspect(&1)}`") + |> to_sentence(final: "and") + + " `#{subject_name}`: #{resources}\n" + end) + + raise """ + Error: There are multiple resources configured with the same subject name. + + This is bad because we will be unable to correctly convert between subjects and resources. + + #{duplicates} + """ + end + @behaviour AshAuthentication.Plug import Plug.Conn diff --git a/lib/ash_authentication/validations.ex b/lib/ash_authentication/validations.ex index 01eae27..cb54f0c 100644 --- a/lib/ash_authentication/validations.ex +++ b/lib/ash_authentication/validations.ex @@ -49,28 +49,6 @@ defmodule AshAuthentication.Validations do {:error, "Expected `#{inspect(field)}` to be present and contain one of #{values}"} end - @doc """ - Validates the uniqueness of all subject names per otp app. - """ - @spec validate_unique_subject_names(module) :: :ok | no_return - def validate_unique_subject_names(otp_app) do - otp_app - |> AshAuthentication.authenticated_resources() - |> Enum.group_by(& &1.subject_name) - |> Enum.each(fn - {subject_name, configs} when length(configs) > 1 -> - resources = - configs - |> Enum.map(&"`#{inspect(&1.resource)}`") - |> AshAuthentication.Utils.to_sentence() - - raise "Error: multiple resources use the `#{subject_name}` subject name: #{resources}" - - _ -> - :ok - end) - end - @doc """ Find and return a named attribute in the DSL state. """