Packages
ash_authentication
5.0.0-rc.5
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/password/verifier.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Strategy.Password.Verifier do
@moduledoc """
DSL verifier for the password strategy.
"""
alias AshAuthentication.{
AddOn.AuditLog.VerifierHelpers,
HashProvider,
Info,
Sender,
Strategy.Password
}
alias Spark.{Dsl.Verifier, Error.DslError}
import AshAuthentication.Validations
@doc false
@spec verify(Password.t(), map) :: :ok | {:error, Exception.t()}
def verify(strategy, dsl_state) do
with :ok <- validate_behaviour(strategy.hash_provider, HashProvider),
:ok <- validate_hash_provider_dep(strategy),
:ok <- validate_hash_provider_entropy(strategy),
:ok <- validate_tokens_enabled_for_sign_in_tokens(dsl_state, strategy),
:ok <- validate_tokens_enabled_for_resettable(dsl_state, strategy),
:ok <- validate_brute_force_strategy(dsl_state, strategy) do
maybe_validate_resettable_sender(dsl_state, strategy)
end
end
defp validate_brute_force_strategy(_dsl_state, %{brute_force_strategy: nil}), do: :ok
defp validate_brute_force_strategy(_dsl_state, %{brute_force_strategy: {:preparation, _}}),
do: :ok
defp validate_brute_force_strategy(
dsl_state,
%{brute_force_strategy: {:audit_log, audit_log_name}} = strategy
) do
with {:ok, audit_log} <-
VerifierHelpers.validate_audit_log_exists(dsl_state, strategy, audit_log_name),
:ok <- maybe_validate_sign_in_audit_log(dsl_state, strategy, audit_log) do
maybe_validate_reset_request_audit_log(dsl_state, strategy, audit_log)
end
end
defp maybe_validate_sign_in_audit_log(dsl_state, strategy, audit_log)
when strategy.sign_in_enabled?,
do:
VerifierHelpers.validate_action_audit_logged(
dsl_state,
strategy,
strategy.sign_in_action_name,
audit_log
)
defp maybe_validate_sign_in_audit_log(_, _, _), do: :ok
defp maybe_validate_reset_request_audit_log(
dsl_state,
%{resettable: %{request_password_reset_action_name: action_name}} = strategy,
audit_log
)
when is_atom(action_name),
do:
VerifierHelpers.validate_action_audit_logged(
dsl_state,
strategy,
action_name,
audit_log
)
defp maybe_validate_reset_request_audit_log(_, _, _), do: :ok
defp validate_hash_provider_dep(strategy)
when strategy.hash_provider == AshAuthentication.BcryptProvider do
if Code.ensure_loaded?(Bcrypt) do
:ok
else
{:error,
DslError.exception(
module: strategy.resource,
path: [
:authentication,
:strategies,
:password,
strategy.name,
:hash_provider
],
message:
"Bcrypt is not available. Please ensure that the Bcrypt library is installed and available."
)}
end
end
defp validate_hash_provider_dep(strategy)
when strategy.hash_provider == AshAuthentication.Argon2Provider do
if Code.ensure_loaded?(Argon2) do
:ok
else
{:error,
DslError.exception(
module: strategy.resource,
path: [
:authentication,
:strategies,
:password,
strategy.name,
:hash_provider
],
message:
"Argon2 is not available. Please ensure that the Argon2 library is installed and available."
)}
end
end
defp validate_hash_provider_dep(_), do: :ok
defp validate_hash_provider_entropy(strategy) do
if function_exported?(strategy.hash_provider, :minimum_entropy, 0) and
strategy.hash_provider.minimum_entropy() > 0 do
{:error,
DslError.exception(
module: strategy.resource,
path: [:authentication, :strategies, :password, strategy.name, :hash_provider],
message: """
The hash provider `#{inspect(strategy.hash_provider)}` requires a minimum entropy \
of #{strategy.hash_provider.minimum_entropy()} bits, but user-chosen passwords cannot \
guarantee a minimum entropy level.
Use a hash provider with `minimum_entropy() == 0` such as \
`AshAuthentication.BcryptProvider` or `AshAuthentication.Argon2Provider`.
"""
)}
else
:ok
end
end
defp validate_tokens_enabled_for_sign_in_tokens(dsl_state, strategy)
when strategy.sign_in_tokens_enabled? do
resource = Verifier.get_persisted(dsl_state, :module)
cond do
!strategy.sign_in_enabled? ->
{:error,
DslError.exception(
module: resource,
path: [
:authentication,
:strategies,
:password,
strategy.name,
:sign_in_tokens_enabled?
],
message: """
The `sign_in_tokens_enabled?` option requires that `sign_in_enabled?` be set to `true`.
"""
)}
!Info.authentication_tokens_enabled?(dsl_state) ->
{:error,
DslError.exception(
module: resource,
path: [
:authentication,
:strategies,
:password,
strategy.name,
:sign_in_tokens_enabled?
],
message: """
The `sign_in_tokens_enabled?` option requires that tokens are enabled for your resource. For example:
authentication do
...
tokens do
enabled? true
end
end
"""
)}
true ->
:ok
end
end
defp validate_tokens_enabled_for_sign_in_tokens(_, _), do: :ok
defp validate_tokens_enabled_for_resettable(dsl_state, %{resettable: resettable, name: name})
when is_struct(resettable) do
resource = Verifier.get_persisted(dsl_state, :module)
if Info.authentication_tokens_enabled?(dsl_state) do
:ok
else
{:error,
DslError.exception(
module: resource,
path: [
:authentication,
:strategies,
:password,
name,
:resettable
],
message: """
The `resettable` option requires that tokens are enabled for your resource. For example:
authentication do
...
tokens do
enabled? true
end
end
"""
)}
end
end
defp validate_tokens_enabled_for_resettable(_, _), do: :ok
defp maybe_validate_resettable_sender(dsl_state, %{resettable: resettable})
when is_struct(resettable) do
with {:ok, {sender, _opts}} <- Map.fetch(resettable, :sender),
:ok <- validate_behaviour(sender, Sender) do
:ok
else
:error ->
resource = Verifier.get_persisted(dsl_state, :module)
{:error,
DslError.exception(
module: resource,
path: [:authentication, :strategies, :password, :resettable],
message: "A `sender` is required."
)}
{:error, reason} ->
{:error, reason}
end
end
defp maybe_validate_resettable_sender(_, _), do: :ok
end