defmodule Eventos.Actors.User do @moduledoc """ Represents a local user """ use Ecto.Schema import Ecto.Changeset alias Eventos.Actors.{Actor, User} schema "users" do field :email, :string field :password_hash, :string field :password, :string, virtual: true field :role, :integer, default: 0 has_many :actors, Actor field :confirmed_at, :utc_datetime field :confirmation_sent_at, :utc_datetime field :confirmation_token, :string field :reset_password_sent_at, :utc_datetime field :reset_password_token, :string timestamps() end @doc false def changeset(%User{} = user, attrs) do user |> cast(attrs, [:email, :role, :password_hash, :confirmed_at, :confirmation_sent_at, :confirmation_token, :reset_password_sent_at, :reset_password_token]) |> validate_required([:email]) |> unique_constraint(:email, [message: "registration.error.email_already_used"]) |> validate_format(:email, ~r/@/) |> validate_length(:password, min: 6, max: 100, message: "registration.error.password_too_short") end def registration_changeset(struct, params) do struct |> changeset(params) |> cast(params, ~w(password)a, []) |> validate_required([:email, :password]) |> validate_length(:password, min: 6, max: 100, message: "registration.error.password_too_short") |> hash_password() |> save_confirmation_token() |> unique_constraint(:confirmation_token, [message: "regisration.error.confirmation_token_already_in_use"]) end def send_password_reset_changeset(%User{} = user, attrs) do user |> cast(attrs, [:reset_password_token, :reset_password_sent_at]) end def password_reset_changeset(%User{} = user, attrs) do user |> cast(attrs, [:password, :reset_password_token, :reset_password_sent_at]) |> validate_length(:password, min: 6, max: 100, message: "registration.error.password_too_short") |> hash_password() end defp save_confirmation_token(changeset) do case changeset do %Ecto.Changeset{valid?: true, changes: %{email: _email}} -> changeset = put_change(changeset, :confirmation_token, random_string(30)) put_change(changeset, :confirmation_sent_at, DateTime.utc_now()) _ -> changeset end end defp random_string(length) do length |> :crypto.strong_rand_bytes() |> Base.url_encode64 end @doc """ Hash password when it's changed """ defp hash_password(changeset) do case changeset do %Ecto.Changeset{valid?: true, changes: %{password: password}} -> put_change(changeset, :password_hash, Comeonin.Argon2.hashpwsalt(password)) _ -> changeset end end def is_confirmed(%User{confirmed_at: nil} = _user) do {:error, :unconfirmed} end def is_confirmed(%User{} = user) do {:ok, user} end end