Packages
ash_authentication
3.2.2
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/strategies/oauth2/transformer.ex
defmodule AshAuthentication.Strategy.OAuth2.Transformer do
@moduledoc """
DSL transformer for oauth2 strategies.
Iterates through any oauth2 strategies and ensures that all the correct
actions and settings are in place.
"""
use Spark.Dsl.Transformer
alias Ash.{Resource, Type}
alias AshAuthentication.{GenerateTokenChange, Info, Strategy.OAuth2}
alias Spark.{Dsl.Transformer, Error.DslError}
import AshAuthentication.Utils
import AshAuthentication.Validations
import AshAuthentication.Validations.Action
@doc false
@impl true
@spec after?(module) :: boolean
def after?(AshAuthentication.Transformer), do: true
def after?(_), do: false
@doc false
@impl true
@spec before?(module) :: boolean
def before?(Resource.Transformers.DefaultAccept), do: true
def before?(_), do: false
@doc false
@impl true
@spec transform(map) ::
:ok
| {:ok, map()}
| {:error, term()}
| {:warn, map(), String.t() | [String.t()]}
| :halt
def transform(dsl_state) do
dsl_state
|> Info.authentication_strategies()
|> Stream.filter(&is_struct(&1, OAuth2))
|> Enum.reduce_while({:ok, dsl_state}, fn strategy, {:ok, dsl_state} ->
case transform_strategy(strategy, dsl_state) do
{:ok, dsl_state} -> {:cont, {:ok, dsl_state}}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
end
defp transform_strategy(strategy, dsl_state) do
with strategy <- set_defaults(strategy),
{:ok, dsl_state} <- maybe_build_identity_relationship(dsl_state, strategy),
:ok <- maybe_validate_register_action(dsl_state, strategy),
:ok <- maybe_validate_sign_in_action(dsl_state, strategy),
{:ok, resource} <- persisted_option(dsl_state, :module) do
strategy = %{strategy | resource: resource}
dsl_state =
dsl_state
|> Transformer.replace_entity(
~w[authentication strategies]a,
strategy,
&(&1.name == strategy.name)
)
|> then(fn dsl_state ->
~w[register_action_name sign_in_action_name]a
|> Stream.map(&Map.get(strategy, &1))
|> Enum.reduce(
dsl_state,
&Transformer.persist(&2, {:authentication_action, &1}, strategy)
)
end)
{:ok, dsl_state}
else
{:error, reason} when is_binary(reason) ->
{:error,
DslError.exception(path: [:authentication, :strategies, strategy.name], message: reason)}
{:error, reason} ->
{:error, reason}
end
end
defp set_defaults(strategy) do
default_secret = {OAuth2.Default, []}
strategy
|> maybe_set_field(:authorize_path, default_secret)
|> maybe_set_field(:token_path, default_secret)
|> maybe_set_field(:user_path, default_secret)
|> maybe_set_field_lazy(:register_action_name, &:"register_with_#{&1.name}")
|> maybe_set_field_lazy(:sign_in_action_name, &:"sign_in_with_#{&1.name}")
end
defp maybe_build_identity_relationship(dsl_state, strategy)
when is_falsy(strategy.identity_resource),
do: {:ok, dsl_state}
defp maybe_build_identity_relationship(dsl_state, strategy) do
maybe_build_relationship(
dsl_state,
strategy.identity_relationship_name,
&build_identity_relationship(&1, strategy)
)
end
defp build_identity_relationship(_dsl_state, strategy) do
Transformer.build_entity(Resource.Dsl, [:relationships], :has_many,
name: strategy.identity_relationship_name,
destination: strategy.identity_resource,
destination_attribute: strategy.identity_relationship_user_id_attribute
)
end
defp maybe_validate_register_action(dsl_state, strategy) when strategy.registration_enabled? do
with {:ok, action} <- validate_action_exists(dsl_state, strategy.register_action_name),
:ok <- validate_action_has_argument(action, :user_info),
:ok <- validate_action_argument_option(action, :user_info, :type, [Type.Map, :map]),
:ok <- validate_action_argument_option(action, :user_info, :allow_nil?, [false]),
:ok <- validate_action_has_argument(action, :oauth_tokens),
:ok <-
validate_action_argument_option(action, :oauth_tokens, :type, [Type.Map, :map]),
:ok <- validate_action_argument_option(action, :oauth_tokens, :allow_nil?, [false]),
:ok <- maybe_validate_action_has_token_change(dsl_state, action),
:ok <- validate_field_in_values(action, :upsert?, [true]),
:ok <-
validate_field_with(
action,
:upsert_identity,
&(is_atom(&1) and not is_falsy(&1)),
"Expected `upsert_identity` to be set"
),
:ok <- maybe_validate_action_has_identity_change(action, strategy) do
:ok
else
:error ->
{:error, "Unable to validate register action"}
{:error, reason} when is_binary(reason) ->
{:error, "`#{inspect(strategy.register_action_name)}` action: #{reason}"}
{:error, reason} ->
{:error, reason}
end
end
defp maybe_validate_register_action(_dsl_state, _strategy), do: :ok
defp maybe_validate_action_has_token_change(dsl_state, action) do
if Info.authentication_tokens_enabled?(dsl_state) do
validate_action_has_change(action, GenerateTokenChange)
else
:ok
end
end
defp maybe_validate_action_has_identity_change(_action, strategy)
when is_falsy(strategy.identity_resource),
do: :ok
defp maybe_validate_action_has_identity_change(action, _strategy),
do: validate_action_has_change(action, OAuth2.IdentityChange)
defp maybe_validate_sign_in_action(_dsl_state, strategy) when strategy.registration_enabled?,
do: :ok
defp maybe_validate_sign_in_action(dsl_state, strategy) do
with {:ok, action} <- validate_action_exists(dsl_state, strategy.sign_in_action_name),
:ok <- validate_action_has_argument(action, :user_info),
:ok <- validate_action_argument_option(action, :user_info, :type, [Ash.Type.Map, :map]),
:ok <- validate_action_argument_option(action, :user_info, :allow_nil?, [false]),
:ok <- validate_action_has_argument(action, :oauth_tokens),
:ok <-
validate_action_argument_option(action, :oauth_tokens, :type, [Ash.Type.Map, :map]),
:ok <- validate_action_argument_option(action, :oauth_tokens, :allow_nil?, [false]),
:ok <- validate_action_has_preparation(action, OAuth2.SignInPreparation) do
:ok
else
:error -> {:error, "Unable to validate sign in action"}
{:error, reason} -> {:error, reason}
end
end
end