Some work

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2018-07-04 14:29:17 +02:00
parent 394057d45e
commit 93a97b0865
56 changed files with 5577 additions and 4327 deletions

View File

@@ -312,8 +312,12 @@ defmodule Eventos.Actors do
Get an user by email
"""
def find_by_email(email) do
user = Repo.get_by(User, email: email)
Repo.preload(user, :actor)
case Repo.get_by(User, email: email) |> Repo.preload(:actor) do
nil ->
{:error, nil}
user ->
{:ok, user}
end
end
@doc """
@@ -366,8 +370,7 @@ defmodule Eventos.Actors do
try do
Eventos.Repo.insert!(actor_with_user)
user = find_by_email(email)
{:ok, user}
find_by_email(email)
rescue
e in Ecto.InvalidChangesetError ->
{:error, e.changeset}

View File

@@ -0,0 +1,29 @@
defmodule Eventos.Actors.Service.Activation do
@moduledoc false
alias Eventos.{Mailer, Repo, Actors.User, Actors}
alias Eventos.Email.User, as: UserEmail
require Logger
@doc false
def check_confirmation_token(token) when is_binary(token) do
with %User{} = user <- Repo.get_by(User, confirmation_token: token) do
Actors.update_user(user, %{"confirmed_at" => DateTime.utc_now(), "confirmation_sent_at" => nil, "confirmation_token" => nil})
else
_err ->
{:error, "Invalid token"}
end
end
def resend_confirmation_email(%User{} = user, locale \\ "en") do
{:ok, user} = Actors.update_user(user, %{"confirmation_sent_at" => DateTime.utc_now()})
send_confirmation_email(user, locale)
end
def send_confirmation_email(%User{} = user, locale \\ "en") do
user
|> UserEmail.confirmation_email(locale)
|> Mailer.deliver_later()
end
end

View File

@@ -0,0 +1,57 @@
defmodule Eventos.Actors.Service.ResetPassword do
@moduledoc false
require Logger
alias Eventos.{Mailer, Repo, Actors.User}
alias Eventos.Email.User, as: UserEmail
@doc """
Check that the provided token is correct and update provided password
"""
@spec check_reset_password_token(String.t, String.t) :: tuple
def check_reset_password_token(password, token) do
with %User{} = user <- Repo.get_by(User, reset_password_token: token) do
User.password_reset_changeset(user, %{"password" => password, "reset_password_sent_at" => nil, "reset_password_token" => nil}) |> Repo.update()
else
_err ->
{:error, :invalid_token}
end
end
@doc """
Send the email reset password, if it's not too soon since the last send
"""
@spec send_password_reset_email(User.t, String.t) :: tuple
def send_password_reset_email(%User{} = user, locale \\ "en") do
with :ok <- we_can_send_email(user),
{:ok, %User{} = user_updated} <- User.send_password_reset_changeset(user, %{"reset_password_token" => random_string(30), "reset_password_sent_at" => DateTime.utc_now()}) |> Repo.update() do
mail = user_updated
|> UserEmail.reset_password_email(locale)
|> Mailer.deliver_later()
{:ok, mail}
else
{:error, reason} -> {:error, reason}
end
end
@spec random_string(integer) :: String.t
defp random_string(length) do
:crypto.strong_rand_bytes(length) |> Base.url_encode64
end
@spec we_can_send_email(User.t) :: boolean
defp we_can_send_email(%User{} = user) do
case user.reset_password_sent_at do
nil ->
:ok
_ ->
case Timex.before?(Timex.shift(user.reset_password_sent_at, hours: 1), DateTime.utc_now()) do
true ->
:ok
false ->
{:error, :email_too_soon}
end
end
end
end

View File

@@ -12,6 +12,11 @@ defmodule Eventos.Actors.User do
field :password, :string, virtual: true
field :role, :integer, default: 0
belongs_to :actor, 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
@@ -19,18 +24,49 @@ defmodule Eventos.Actors.User do
@doc false
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [:email, :role, :password_hash, :actor_id])
|> cast(attrs, [:email, :role, :password_hash, :actor_id, :confirmed_at, :confirmation_sent_at, :confirmation_token, :reset_password_sent_at, :reset_password_token])
|> validate_required([:email])
|> unique_constraint(: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_length(:password, min: 6, max: 100)
|> 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
:crypto.strong_rand_bytes(length) |> Base.url_encode64
end
@doc """
@@ -47,4 +83,12 @@ defmodule Eventos.Actors.User do
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