Packages
pow
1.0.30
1.0.39
1.0.38
1.0.37
1.0.36
1.0.35
1.0.34
1.0.33
1.0.32
1.0.31
1.0.30
1.0.29
1.0.28
1.0.27
1.0.26
1.0.25
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.1.0-rc.1
0.1.0-alpha.8
0.1.0-alpha.7
retired
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha
Robust user authentication solution
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/extensions/email_confirmation/ecto/schema.ex
defmodule PowEmailConfirmation.Ecto.Schema do
@moduledoc """
Handles the e-mail confirmation schema for user.
## Customize PowEmailConfirmation fields
If you need to modify any of the fields that `PowEmailConfirmation` adds to
the user schema, you can override them by defining them before
`pow_user_fields/0`:
defmodule MyApp.Users.User do
use Ecto.Schema
use Pow.Ecto.Schema
use Pow.Extension.Ecto.Schema,
extensions: [PowEmailConfirmation]
schema "users" do
field :email_confirmation_token, :string
field :email_confirmed_at, :utc_datetime
field :unconfirmed_email, :string
pow_user_fields()
timestamps()
end
end
"""
use Pow.Extension.Ecto.Schema.Base
alias Ecto.Changeset
alias Pow.{Config, Extension.Ecto.Schema, UUID}
@doc false
@impl true
def validate!(_config, module) do
Schema.require_schema_field!(module, :email, PowEmailConfirmation)
end
@doc false
@impl true
def attrs(_config) do
[
{:email_confirmation_token, :string},
{:email_confirmed_at, :utc_datetime},
{:unconfirmed_email, :string}
]
end
@doc false
@impl true
def indexes(_config) do
[{:email_confirmation_token, true}]
end
@doc false
@impl true
defmacro __using__(_config) do
quote do
def confirm_email_changeset(changeset, attrs), do: pow_confirm_email_changeset(changeset, attrs)
defdelegate pow_confirm_email_changeset(changeset, attrs), to: unquote(__MODULE__), as: :confirm_email_changeset
defoverridable confirm_email_changeset: 2
end
end
@doc """
Handles e-mail confirmation if e-mail is updated.
The `:email_confirmation_token` will always be set if the struct isn't
persisted to the database.
For structs persisted to the database, no changes will happen if there is no
`:email` in the params. Likewise, no changes will happen if the `:email`
change is the same as the persisted `:unconfirmed_email` value.
If the `:email` change is the same as the persisted `:email` value then both
`:email_confirmation_token` and `:unconfirmed_email` will be set to nil.
Otherwise the `:email` change will be copied over to `:unconfirmed_email` and
the `:email` change will be reverted back to the original persisted `:email`
value. A unique `:email_confirmation_token` will be generated.
"""
@impl true
@spec changeset(Changeset.t(), map(), Config.t()) :: Changeset.t()
def changeset(%{valid?: true} = changeset, attrs, _config) do
cond do
built?(changeset) ->
put_email_confirmation_token(changeset)
email_reverted?(changeset, attrs) ->
changeset
|> Changeset.put_change(:email_confirmation_token, nil)
|> Changeset.put_change(:unconfirmed_email, nil)
email_changed?(changeset) ->
current_email = changeset.data.email
changed_email = Changeset.get_field(changeset, :email)
changeset = set_unconfirmed_email(changeset, current_email, changed_email)
case unconfirmed_email_changed?(changeset) do
true -> put_email_confirmation_token(changeset)
false -> changeset
end
true ->
changeset
end
end
def changeset(changeset, _attrs, _config), do: changeset
defp built?(changeset), do: Ecto.get_meta(changeset.data, :state) == :built
defp email_reverted?(changeset, attrs) do
param = Map.get(attrs, :email) || Map.get(attrs, "email")
current = changeset.data.email
param == current
end
defp email_changed?(changeset) do
case Changeset.get_change(changeset, :email) do
nil -> false
_any -> true
end
end
defp put_email_confirmation_token(changeset) do
changeset
|> Changeset.put_change(:email_confirmation_token, UUID.generate())
|> Changeset.unique_constraint(:email_confirmation_token)
end
defp set_unconfirmed_email(changeset, current_email, new_email) do
changeset
|> Changeset.put_change(:email, current_email)
|> Changeset.put_change(:unconfirmed_email, new_email)
end
defp unconfirmed_email_changed?(changeset) do
case Changeset.get_change(changeset, :unconfirmed_email) do
nil -> false
_any -> true
end
end
@doc """
Sets the e-mail as confirmed.
This updates `:email_confirmed_at` and sets `:email_confirmation_token` to
nil.
If the struct has a `:unconfirmed_email` value, then the `:email` will be
changed to this value, and `:unconfirmed_email` will be set to nil.
"""
@spec confirm_email_changeset(Ecto.Schema.t() | Changeset.t(), map()) :: Changeset.t()
def confirm_email_changeset(%Changeset{data: %{unconfirmed_email: unconfirmed_email}} = changeset, params) when not is_nil(unconfirmed_email) do
confirm_email(changeset, unconfirmed_email, params)
end
def confirm_email_changeset(%Changeset{data: %{email_confirmed_at: nil, email: email}} = changeset, params) do
confirm_email(changeset, email, params)
end
def confirm_email_changeset(%Changeset{} = changeset, _params), do: changeset
def confirm_email_changeset(user, params) do
user
|> Changeset.change()
|> confirm_email_changeset(params)
end
defp confirm_email(changeset, email, _params) do
confirmed_at = Pow.Ecto.Schema.__timestamp_for__(changeset.data.__struct__, :email_confirmed_at)
changes =
[
email_confirmed_at: confirmed_at,
email: email,
unconfirmed_email: nil,
email_confirmation_token: nil
]
changeset
|> Changeset.change(changes)
|> Changeset.unique_constraint(:email)
end
# TODO: Remove by 1.1.0
@deprecated "Use `confirm_email_changeset/2` instead"
@doc false
def confirm_email_changeset(changeset), do: confirm_email_changeset(changeset, %{})
end