fix(Confirmation): send the original changeset to confirmation senders. (#132)

Changes the behaviour of the `ConfirmationHookChange` to pass the original, unmodified changeset in the sender options so that senders can account for inhibited changes.
This commit is contained in:
James Harton 2023-01-13 13:30:58 +13:00 committed by GitHub
parent 0bc98800c0
commit 085d640c44
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 8 deletions

View file

@ -78,7 +78,7 @@ defmodule AshAuthentication.AddOn.Confirmation.ConfirmationHookChange do
|> case do
{:ok, token} ->
{sender, send_opts} = strategy.sender
sender.send(user, token, send_opts)
sender.send(user, token, Keyword.put(send_opts, :changeset, original_changeset))
metadata =
user.__metadata__

View file

@ -669,7 +669,7 @@ defmodule AshAuthentication.Dsl do
sender: [
type:
{:spark_function_behaviour, AshAuthentication.Sender,
{AshAuthentication.SenderFunction, 2}},
{AshAuthentication.SenderFunction, 3}},
doc: """
How to send the confirmation instructions to the user.
@ -681,6 +681,11 @@ defmodule AshAuthentication.Dsl do
Accepts a module, module and opts, or a function that takes a record,
reset token and options.
The options will be a keyword list containing the original
changeset, before any changes were inhibited. This allows you
to send an email to the user's new email address if it is being
changed for example.
See `AshAuthentication.Sender` for more information.
""",
required: true

View file

@ -18,8 +18,8 @@ defmodule AshAuthentication.SenderFunction do
{fun, opts} when is_function(fun, 3) ->
fun.(user, token, opts)
{{m, f, a}, _opts} ->
apply(m, f, [user, token | a])
{{m, f, a}, opts} ->
apply(m, f, [user, token, Keyword.merge(a, opts)])
{nil, opts} ->
raise "Invalid options given to `send/3` callback: `#{inspect(opts)}`."

View file

@ -58,7 +58,7 @@ defmodule AshAuthentication.Strategy.Password.Resettable do
sender: [
type:
{:spark_function_behaviour, AshAuthentication.Sender,
{AshAuthentication.SenderFunction, 2}},
{AshAuthentication.SenderFunction, 3}},
doc: """
How to send the password reset instructions to the user.

View file

@ -0,0 +1,24 @@
defmodule AshAuthentication.AddOn.Confirmation.ConfirmationHookChangeTest do
@moduledoc false
use DataCase, async: false
import ExUnit.CaptureLog
describe "when creating a new user" do
test "it always sends a confirmation" do
username = username()
assert capture_log(fn -> build_user(username: username) end) =~
~r/Confirmation request for user #{username}/
end
end
describe "when updating an existing user" do
test "it sends a confirmation for the new username" do
user = build_user()
new_username = username()
assert capture_log(fn -> Example.User.update_user!(user, %{username: new_username}) end) =~
~r/Confirmation request for user #{new_username}/
end
end
end

View file

@ -90,6 +90,11 @@ defmodule Example.User do
end
end
code_interface do
define_for Example
define :update_user, action: :update
end
graphql do
type :user
@ -136,8 +141,13 @@ defmodule Example.User do
monitor_fields [:username]
inhibit_updates? true
sender fn user, token ->
Logger.debug("Confirmation request for user #{user.username}, token #{inspect(token)}")
sender fn _user, token, opts ->
username =
opts
|> Keyword.fetch!(:changeset)
|> Ash.Changeset.get_attribute(:username)
Logger.debug("Confirmation request for user #{username}, token #{inspect(token)}")
end
end
end
@ -145,7 +155,7 @@ defmodule Example.User do
strategies do
password do
resettable do
sender fn user, token ->
sender fn user, token, _opts ->
Logger.debug(
"Password reset request for user #{user.username}, token #{inspect(token)}"
)