AshAshPhoenixBread Crumbs:
> action validation {CauseBeacon.Accounts.User.PasswordValidator, [field: :password]}
> building changeset for CauseBeacon.Accounts.User.update_password
Unknown Error
* ** (CaseClauseError) no case clause matching:
[field: :password]
(ash 3.12.0) lib/ash/changeset/changeset.ex:3915: Ash.Changeset.do_validation/5
(ash 3.12.0) lib/ash/changeset/changeset.ex:3150: anonymous fn/6 in Ash.Changeset.run_action_changes/6
(elixir 1.19.5) lib/enum.ex:2520: Enum."-reduce/3-lists^foldl/2-0-"/3
(ash 3.12.0) lib/ash/changeset/changeset.ex:3146: Ash.Changeset.run_action_changes/6
(ash 3.12.0) lib/ash/changeset/changeset.ex:2577: Ash.Changeset.do_for_action/4
(ash_phoenix 2.3.19) lib/ash_phoenix/form/form.ex:805: AshPhoenix.Form.for_update/3
(cause_beacon 0.1.0) lib/cause_beacon_web/live/my_profile_live/password_change_form.ex:92: CauseBeaconWeb.MyProfileLive.PasswordChangeForm.assign_form/1
(cause_beacon 0.1.0) lib/cause_beacon_web/live/my_profile_live/password_change_form.ex:10: CauseBeaconWeb.MyProfileLive.PasswordChangeForm.update/2Bread Crumbs:
> action validation {CauseBeacon.Accounts.User.PasswordValidator, [field: :password]}
> building changeset for CauseBeacon.Accounts.User.update_password
Unknown Error
* ** (CaseClauseError) no case clause matching:
[field: :password]
(ash 3.12.0) lib/ash/changeset/changeset.ex:3915: Ash.Changeset.do_validation/5
(ash 3.12.0) lib/ash/changeset/changeset.ex:3150: anonymous fn/6 in Ash.Changeset.run_action_changes/6
(elixir 1.19.5) lib/enum.ex:2520: Enum."-reduce/3-lists^foldl/2-0-"/3
(ash 3.12.0) lib/ash/changeset/changeset.ex:3146: Ash.Changeset.run_action_changes/6
(ash 3.12.0) lib/ash/changeset/changeset.ex:2577: Ash.Changeset.do_for_action/4
(ash_phoenix 2.3.19) lib/ash_phoenix/form/form.ex:805: AshPhoenix.Form.for_update/3
(cause_beacon 0.1.0) lib/cause_beacon_web/live/my_profile_live/password_change_form.ex:92: CauseBeaconWeb.MyProfileLive.PasswordChangeForm.assign_form/1
(cause_beacon 0.1.0) lib/cause_beacon_web/live/my_profile_live/password_change_form.ex:10: CauseBeaconWeb.MyProfileLive.PasswordChangeForm.update/2defmodule CauseBeacon.Accounts.User.PasswordValidator do
@moduledoc """
A validator module to ensure that a given string is a good password.
"""
use Ash.Resource.Validation
@key :field
@impl true
@spec init(keyword()) :: keyword() | {:error, String.t()}
def init(opts) do
dbg(opts)
case opts[@key] do
nil -> {:error, "requires a :field option"}
_ -> opts
end
end
@impl true
@spec validate(Ash.Subject.t(), keyword(), map()) :: :ok | {:error, String.t()}
def validate(subject, opts, _context) do
value = Ash.Subject.get_argument_or_attribute(subject, opts[@key])
if value &&
Regex.match?(
~r/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{1,}$/,
value
) do
:ok
else
{:error,
Ash.Error.Changes.InvalidAttribute.exception(
field: opts[@key],
message: "must include 1 lowercase, 1 uppercase, 1 number, and 1 special character"
)}
end
end
enddefmodule CauseBeacon.Accounts.User.PasswordValidator do
@moduledoc """
A validator module to ensure that a given string is a good password.
"""
use Ash.Resource.Validation
@key :field
@impl true
@spec init(keyword()) :: keyword() | {:error, String.t()}
def init(opts) do
dbg(opts)
case opts[@key] do
nil -> {:error, "requires a :field option"}
_ -> opts
end
end
@impl true
@spec validate(Ash.Subject.t(), keyword(), map()) :: :ok | {:error, String.t()}
def validate(subject, opts, _context) do
value = Ash.Subject.get_argument_or_attribute(subject, opts[@key])
if value &&
Regex.match?(
~r/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{1,}$/,
value
) do
:ok
else
{:error,
Ash.Error.Changes.InvalidAttribute.exception(
field: opts[@key],
message: "must include 1 lowercase, 1 uppercase, 1 number, and 1 special character"
)}
end
end
endupdate :update_password do
accept []
require_atomic? false
description "Updates a user's password"
argument :current_password, :string do
description "The current password for the user"
allow_nil? false
sensitive? true
end
argument :password, :string do
description "The proposed password for the user, in plain text."
allow_nil? false
constraints min_length: 8
sensitive? true
end
argument :password_confirmation, :string do
description "The proposed password for the user (again), in plain text."
allow_nil? false
sensitive? true
end
validate confirm(:password, :password_confirmation)
validate {CauseBeacon.Accounts.User.PasswordValidator, field: :password}
# only_when_valid? helps to stop crashing when current_password is empty
validate {AshAuthentication.Strategy.Password.PasswordValidation,
strategy_name: :password, password_argument: :current_password},
only_when_valid?: true
change {AshAuthentication.Strategy.Password.HashPasswordChange, strategy_name: :password}
endupdate :update_password do
accept []
require_atomic? false
description "Updates a user's password"
argument :current_password, :string do
description "The current password for the user"
allow_nil? false
sensitive? true
end
argument :password, :string do
description "The proposed password for the user, in plain text."
allow_nil? false
constraints min_length: 8
sensitive? true
end
argument :password_confirmation, :string do
description "The proposed password for the user (again), in plain text."
allow_nil? false
sensitive? true
end
validate confirm(:password, :password_confirmation)
validate {CauseBeacon.Accounts.User.PasswordValidator, field: :password}
# only_when_valid? helps to stop crashing when current_password is empty
validate {AshAuthentication.Strategy.Password.PasswordValidation,
strategy_name: :password, password_argument: :current_password},
only_when_valid?: true
change {AshAuthentication.Strategy.Password.HashPasswordChange, strategy_name: :password}
end