improvement: move subject_name uniqueness validation to compile time.

This commit is contained in:
James Harton 2022-10-26 12:52:50 +13:00
parent 13d3f0d2ab
commit aa638e0230
2 changed files with 39 additions and 25 deletions

View file

@ -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

View file

@ -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.
"""