Packages
ash_authentication
5.0.0-rc.9
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.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash_authentication/add_ons/audit_log/transformer.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.AddOn.AuditLog.Transformer do
@moduledoc false
use Spark.Dsl.Transformer
alias Spark.Dsl.Transformer
@doc false
@impl true
def after?(AshAuthentication.Strategy.Custom.Transformer), do: true
def after?(_), do: false
@doc false
@impl true
def before?(_), do: false
@doc false
@impl true
def transform(dsl) do
dsl
|> AshAuthentication.Info.list_strategies()
|> Enum.filter(&is_struct(&1, AshAuthentication.AddOn.AuditLog))
|> Enum.reduce_while({:ok, dsl}, fn strategy, {:ok, dsl} ->
case transform(strategy, dsl) do
{:ok, dsl} -> {:cont, {:ok, dsl}}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
end
defp transform(strategy, dsl) do
with {:ok, strategy, dsl} <- prefill_included(strategy, dsl),
{:ok, logged_actions} <- find_logged_actions(strategy, dsl),
{:ok, dsl} <- persist_logged_actions(strategy, dsl, logged_actions),
{:ok, dsl} <- persist_logged_fields(strategy, dsl, logged_actions),
{:ok, dsl} <- add_global_change(strategy, dsl) do
add_global_preparation(strategy, dsl)
end
end
defp prefill_included(strategy, dsl) do
with {:ok, strategy} <- prefill_included_strategies(strategy, dsl),
{:ok, strategy} <- prefill_included_actions(strategy, dsl) do
{:ok, strategy,
Transformer.replace_entity(
dsl,
[:authentication, :add_ons],
strategy,
&(&1.name == strategy.name)
)}
end
end
defp prefill_included_strategies(strategy, dsl) when strategy.include_strategies == [:*] do
strategy_names =
dsl
|> AshAuthentication.Info.list_strategies()
|> Enum.map(& &1.name)
{:ok, %{strategy | include_strategies: strategy_names}}
end
defp prefill_included_strategies(strategy, _dsl), do: {:ok, strategy}
defp prefill_included_actions(strategy, dsl) when strategy.include_actions == [:*] do
action_names =
dsl
|> Ash.Resource.Info.actions()
|> Enum.map(& &1.name)
{:ok, %{strategy | include_actions: action_names}}
end
defp prefill_included_actions(strategy, _dsl), do: {:ok, strategy}
defp find_logged_actions(strategy, dsl) do
logged_actions =
strategy.include_actions
|> Stream.map(fn action_name ->
# For actions that belong to a strategy, use the strategy name
# For actions that don't belong to any strategy, use :audit_log as the strategy
case AshAuthentication.Info.strategy_for_action(dsl, action_name) do
{:ok, action_strategy} -> {action_name, action_strategy.name}
:error -> {action_name, :audit_log}
end
end)
|> Stream.reject(&(elem(&1, 0) in strategy.exclude_actions))
|> Stream.reject(&(elem(&1, 1) in strategy.exclude_strategies))
|> Enum.to_list()
{:ok, logged_actions}
end
defp persist_logged_actions(strategy, dsl, logged_actions) do
dsl =
dsl
|> Transformer.persist({:audit_log, strategy.name, :actions}, logged_actions)
{:ok, dsl}
end
defp persist_logged_fields(strategy, dsl, logged_actions) do
attributes = Ash.Resource.Info.attributes(dsl)
actions = Ash.Resource.Info.actions(dsl)
# Extract just the action names from the tuples
logged_action_names = Enum.map(logged_actions, &elem(&1, 0))
actions
|> Enum.filter(&(&1.name in logged_action_names))
|> Enum.reduce({:ok, dsl}, fn action, {:ok, dsl} ->
argument_names =
action.arguments
|> Enum.filter(&include_in_fields?(&1, strategy))
|> Enum.map(& &1.name)
attribute_names =
attributes
|> Enum.filter(&include_in_fields?(&1, strategy))
|> Enum.map(& &1.name)
|> Enum.reject(&(&1 in argument_names))
dsl =
dsl
|> Transformer.persist(
{:audit_log, strategy.name, action.name, :arguments},
argument_names
)
|> Transformer.persist(
{:audit_log, strategy.name, action.name, :attributes},
attribute_names
)
{:ok, dsl}
end)
end
defp include_in_fields?(arg_or_attr, strategy) do
cond do
arg_or_attr.public? && !arg_or_attr.sensitive? -> true
arg_or_attr.name in strategy.include_fields -> true
true -> false
end
end
defp add_global_change(strategy, dsl) do
with {:ok, change} <-
Transformer.build_entity(Ash.Resource.Dsl, [:changes], :change,
change: {AshAuthentication.AddOn.AuditLog.Auditor.Change, strategy: strategy.name},
on: [:create, :update, :destroy]
) do
{:ok, Transformer.add_entity(dsl, [:changes], change)}
end
end
defp add_global_preparation(strategy, dsl) do
with {:ok, prep} <-
Transformer.build_entity(Ash.Resource.Dsl, [:preparations], :prepare,
preparation:
{AshAuthentication.AddOn.AuditLog.Auditor.Preparation, strategy: strategy.name},
on: [:read, :action]
) do
{:ok, Transformer.add_entity(dsl, [:preparations], prep)}
end
end
end