Current section

Files

Jump to
ash_authentication lib ash_authentication strategies password verifier.ex
Raw

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.{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_tokens_enabled_for_sign_in_tokens(dsl_state, strategy),
:ok <- validate_tokens_enabled_for_resettable(dsl_state, strategy) do
maybe_validate_resettable_sender(dsl_state, strategy)
end
end
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_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