Packages
ash_authentication
4.14.0
5.0.0-rc.12
5.0.0-rc.11
5.0.0-rc.10
5.0.0-rc.9
5.0.0-rc.8
5.0.0-rc.7
5.0.0-rc.6
5.0.0-rc.5
5.0.0-rc.4
5.0.0-rc.3
5.0.0-rc.2
5.0.0-rc.1
5.0.0-rc.0
4.14.1
4.14.0
4.13.7
4.13.6
4.13.5
4.13.4
4.13.3
4.13.2
retired
4.13.1
retired
4.13.0
4.12.0
4.11.0
4.10.0
4.9.9
4.9.8
4.9.7
4.9.6
4.9.5
4.9.4
4.9.3
4.9.2
4.9.1
4.9.0
4.8.7
4.8.6
4.8.5
4.8.3
4.8.2
4.8.1
4.8.0
4.7.6
4.7.5
4.7.4
4.7.3
4.7.2
4.7.1
4.7.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.6
4.5.5
4.5.4
4.5.3
4.5.2
4.5.1
4.5.0
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.12
4.3.11
4.3.10
4.3.9
4.3.8
4.3.7
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.4
4.0.3
4.0.2
4.0.1
4.0.0
4.0.0-rc.6
4.0.0-rc.5
4.0.0-rc.3
4.0.0-rc.2
4.0.0-rc.1
4.0.0-rc.0
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.16
3.11.15
3.11.14
3.11.13
3.11.12
3.11.11
3.11.10
3.11.9
3.11.8
3.11.7
3.11.6
3.11.5
3.11.4
3.11.3
3.11.2
3.11.1
3.11.0
3.10.8
3.10.7
3.10.6
3.10.5
3.10.4
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.0
3.7.9
3.7.8
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.1
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.3.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.0
3.0.3
Authentication extension for the Ash Framework.
Current section
Files
Jump to
Current section
Files
lib/ash_authentication/validations/action.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Validations.Action do
@moduledoc """
Validation helpers for Resource actions.
"""
import AshAuthentication.Utils
alias Ash.Resource.{Actions, Info}
alias Spark.Error.DslError
@doc """
Validate that a named action actually exists.
"""
@spec validate_action_exists(map, atom) ::
{:ok, Actions.action()} | {:error, Exception.t() | String.t()}
def validate_action_exists(dsl_state, action_name) do
case Info.action(dsl_state, action_name) do
action when is_map(action) ->
{:ok, action}
_ ->
{:error,
DslError.exception(
path: [:actions],
message: "Expected an action named `#{inspect(action_name)}` to be present"
)}
end
end
@doc """
Validate an action's argument has an option set to one of the provided values.
"""
@spec validate_action_argument_option(Actions.action(), atom, atom, [any]) ::
:ok | {:error, Exception.t() | String.t()}
def validate_action_argument_option(action, argument_name, field, values) do
with argument when is_map(argument) <-
Enum.find(action.arguments, :missing_argument, &(&1.name == argument_name)),
{:ok, value} <- Map.fetch(argument, field),
true <- value in values do
:ok
else
:missing_argument ->
{:error,
DslError.exception(
path: [:actions, :argument],
message:
"The action `#{inspect(action.name)}` should have an argument named `#{inspect(argument_name)}`"
)}
:error ->
{:error,
DslError.exception(
path: [:actions, :argument],
message:
"The argument `#{inspect(argument_name)}` on action `#{inspect(action.name)}` is missing the `#{inspect(field)}` property"
)}
false ->
case values do
[] ->
{:error,
DslError.exception(
path: [:actions, :argument],
message:
"The argument `#{inspect(argument_name)}` on action `#{inspect(action.name)}` should not have `#{inspect(field)}` set"
)}
[expected] ->
{:error,
DslError.exception(
path: [:actions, :argument],
message:
"The argument `#{inspect(argument_name)}` on action `#{inspect(action.name)}` should have `#{inspect(field)}` set to `#{inspect(expected)}`"
)}
expected ->
expected =
expected
|> Enum.map(&"`#{inspect(&1)}`")
|> to_sentence(final: "or")
{:error,
DslError.exception(
path: [:actions, :argument],
message:
"The argument `#{inspect(argument_name)}` on action `#{inspect(action.name)}` should have `#{inspect(field)}` set to one of #{expected}"
)}
end
end
end
@doc """
Validate the presence of an argument on an action.
"""
@spec validate_action_has_argument(Actions.action(), atom) :: :ok | {:error, Exception.t()}
def validate_action_has_argument(action, argument_name) do
if Enum.any?(action.arguments, &(&1.name == argument_name)),
do: :ok,
else:
{:error,
DslError.exception(
path: [:actions, :argument],
message:
"Expected the action `#{inspect(action.name)}` to have an argument named `#{inspect(argument_name)}`."
)}
end
@doc """
Validate the presence of the named change module on an action.
"""
@spec validate_action_has_change(Actions.action(), module) ::
:ok | {:error, Exception.t()}
def validate_action_has_change(action, change_module) do
has_change? =
action
|> Map.get(:changes, [])
|> Enum.map(&Map.get(&1, :change))
|> Enum.reject(&is_nil/1)
|> Enum.any?(fn {module, opts} ->
cond do
module == change_module ->
true
module == AshEvents.Events.ReplayChangeWrapper ->
wrapped_change = opts[:change].change |> elem(0)
wrapped_change == change_module
true ->
false
end
end)
if has_change?,
do: :ok,
else:
{:error,
DslError.exception(
path: [:actions, :change],
message:
"The action `#{inspect(action.name)}` should have the `#{inspect(change_module)}` change present."
)}
end
@doc """
Validate the presence of the named manual module on an action.
"""
@spec validate_action_has_manual(Actions.action(), module) ::
:ok | {:error, Exception.t()}
def validate_action_has_manual(action, manual_module) do
has_manual? =
action
|> Map.get(:manual)
|> then(fn {module, _args} ->
module == manual_module
end)
if has_manual?,
do: :ok,
else:
{:error,
DslError.exception(
path: [:actions, :manual],
message:
"The action `#{inspect(action.name)}` should have the `#{inspect(manual_module)}` manual present."
)}
end
@doc """
Validate the presence of the named validation module on an action.
"""
@spec validate_action_has_validation(Actions.action(), module) ::
:ok | {:error, Exception.t()}
def validate_action_has_validation(action, validation_module) do
has_validation? =
action
|> Map.get(:changes, [])
|> Enum.map(&Map.get(&1, :validation))
|> Enum.reject(&is_nil/1)
|> Enum.any?(&(elem(&1, 0) == validation_module))
if has_validation?,
do: :ok,
else:
{:error,
DslError.exception(
path: [:actions, :validation],
message:
"The action `#{inspect(action.name)}` should have the `#{inspect(validation_module)}` validation present."
)}
end
@doc """
Validate the presence of the named preparation module on an action.
"""
@spec validate_action_has_preparation(Actions.action(), module) ::
:ok | {:error, Exception.t()}
def validate_action_has_preparation(action, preparation_module) do
has_preparation? =
action
|> Map.get(:preparations, [])
|> Enum.map(&Map.get(&1, :preparation))
|> Enum.reject(&is_nil/1)
|> Enum.any?(&(elem(&1, 0) == preparation_module))
if has_preparation?,
do: :ok,
else:
{:error,
DslError.exception(
path: [:actions, :preparation],
message:
"The action `#{inspect(action.name)}` should have the `#{inspect(preparation_module)}` preparation present."
)}
end
@doc """
Validate the action has the provided option.
"""
@spec validate_action_option(Actions.action(), atom, [any]) :: :ok | {:error, Exception.t()}
def validate_action_option(action, field, values) do
with {:ok, value} <- Map.fetch(action, field),
true <- value in values do
:ok
else
:error ->
{:error,
DslError.exception(
path: [:actions, action.name, field],
message:
"The action `#{inspect(action.name)}` is missing the `#{inspect(field)}` option set"
)}
false ->
case values do
[] ->
{:error,
DslError.exception(
path: [:actions, action.name, field],
message:
"The action `#{inspect(action.name)}` should not have the `#{inspect(field)}` option set"
)}
[expected] ->
{:error,
DslError.exception(
path: [:actions, action.name, field],
message:
"The action `#{inspect(action.name)}` should have the `#{inspect(field)}` option set to `#{inspect(expected)}`"
)}
expected ->
expected = expected |> Enum.map(&"`#{inspect(&1)}`") |> to_sentence(final: "or")
{:error,
DslError.exception(
path: [:actions, action.name, field],
message:
"The action `#{inspect(action.name)}` should have the `#{inspect(field)}` option set to one of #{expected}"
)}
end
end
end
end