@@ -11,6 +11,14 @@ defmodule Mobilizon.Actors do
|
||||
|
||||
alias Mobilizon.Service.ActivityPub
|
||||
|
||||
def data() do
|
||||
Dataloader.Ecto.new(Repo, query: &query/2)
|
||||
end
|
||||
|
||||
def query(queryable, _params) do
|
||||
queryable
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list of actors.
|
||||
|
||||
@@ -42,6 +50,28 @@ defmodule Mobilizon.Actors do
|
||||
Repo.get!(Actor, id)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the associated actor for an user, either the default set one or the first found
|
||||
"""
|
||||
@spec get_actor_for_user(%Mobilizon.Actors.User{}) :: %Mobilizon.Actors.Actor{}
|
||||
def get_actor_for_user(%Mobilizon.Actors.User{} = user) do
|
||||
case user.default_actor_id do
|
||||
nil -> get_first_actor_for_user(user)
|
||||
actor_id -> get_actor!(actor_id)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the first actor found for an user
|
||||
|
||||
Useful when the user has not defined default actor
|
||||
|
||||
Raises `Ecto.NoResultsError` if no Actor is found for this ID
|
||||
"""
|
||||
defp get_first_actor_for_user(%Mobilizon.Actors.User{id: id} = _user) do
|
||||
Repo.one!(from(a in Actor, where: a.user_id == ^id))
|
||||
end
|
||||
|
||||
def get_actor_with_everything!(id) do
|
||||
actor = Repo.get!(Actor, id)
|
||||
Repo.preload(actor, :organized_events)
|
||||
@@ -162,10 +192,13 @@ defmodule Mobilizon.Actors do
|
||||
Repo.all(User)
|
||||
end
|
||||
|
||||
def list_users_with_actors do
|
||||
users = Repo.all(User)
|
||||
Repo.preload(users, :actors)
|
||||
end
|
||||
@doc """
|
||||
List users with their associated actors. No reason for that, so removed
|
||||
"""
|
||||
# def list_users_with_actors do
|
||||
# users = Repo.all(User)
|
||||
# Repo.preload(users, :actors)
|
||||
# end
|
||||
|
||||
defp blank?(""), do: nil
|
||||
defp blank?(n), do: n
|
||||
@@ -226,6 +259,14 @@ defmodule Mobilizon.Actors do
|
||||
Repo.preload(user, :actors)
|
||||
end
|
||||
|
||||
@spec get_user_with_actor(integer()) :: %User{}
|
||||
def get_user_with_actor(id) do
|
||||
case Repo.get(User, id) do
|
||||
nil -> {:error, "User with ID #{id} not found"}
|
||||
user -> {:ok, Repo.preload(user, :actors)}
|
||||
end
|
||||
end
|
||||
|
||||
def get_actor_by_url(url) do
|
||||
Repo.get_by(Actor, url: url)
|
||||
end
|
||||
@@ -297,10 +338,17 @@ defmodule Mobilizon.Actors do
|
||||
@doc """
|
||||
Find actors by their name or displayed name
|
||||
"""
|
||||
def find_actors_by_username_or_name(username) do
|
||||
def find_actors_by_username_or_name(username, page \\ 1, limit \\ 10)
|
||||
def find_actors_by_username_or_name("", page, limit), do: []
|
||||
|
||||
def find_actors_by_username_or_name(username, page, limit) do
|
||||
start = (page - 1) * limit
|
||||
|
||||
Repo.all(
|
||||
from(
|
||||
a in Actor,
|
||||
limit: ^limit,
|
||||
offset: ^start,
|
||||
where:
|
||||
ilike(a.preferred_username, ^like_sanitize(username)) or
|
||||
ilike(a.name, ^like_sanitize(username))
|
||||
@@ -340,19 +388,6 @@ defmodule Mobilizon.Actors do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get an user by email
|
||||
"""
|
||||
def find_by_email(email) do
|
||||
case Repo.preload(Repo.get_by(User, email: email), :actors) do
|
||||
nil ->
|
||||
{:error, nil}
|
||||
|
||||
user ->
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Authenticate user
|
||||
"""
|
||||
@@ -390,31 +425,37 @@ defmodule Mobilizon.Actors do
|
||||
nil
|
||||
end
|
||||
|
||||
actor =
|
||||
Mobilizon.Actors.Actor.registration_changeset(%Mobilizon.Actors.Actor{}, %{
|
||||
preferred_username: username,
|
||||
domain: nil,
|
||||
keys: pem,
|
||||
avatar_url: avatar
|
||||
})
|
||||
|
||||
user =
|
||||
Mobilizon.Actors.User.registration_changeset(%Mobilizon.Actors.User{}, %{
|
||||
email: email,
|
||||
password: password
|
||||
})
|
||||
|
||||
actor_with_user = Ecto.Changeset.put_assoc(actor, :user, user)
|
||||
|
||||
try do
|
||||
Mobilizon.Repo.insert!(actor_with_user)
|
||||
find_by_email(email)
|
||||
rescue
|
||||
e in Ecto.InvalidChangesetError ->
|
||||
{:error, e.changeset}
|
||||
with actor_changeset <-
|
||||
Mobilizon.Actors.Actor.registration_changeset(%Mobilizon.Actors.Actor{}, %{
|
||||
preferred_username: username,
|
||||
domain: nil,
|
||||
keys: pem,
|
||||
avatar_url: avatar
|
||||
}),
|
||||
{:ok, %Mobilizon.Actors.Actor{id: id} = actor} <- Mobilizon.Repo.insert(actor_changeset),
|
||||
user_changeset <-
|
||||
Mobilizon.Actors.User.registration_changeset(%Mobilizon.Actors.User{}, %{
|
||||
email: email,
|
||||
password: password,
|
||||
default_actor_id: id
|
||||
}),
|
||||
{:ok, %Mobilizon.Actors.User{} = user} <- Mobilizon.Repo.insert(user_changeset) do
|
||||
{:ok, Map.put(actor, :user, user)}
|
||||
else
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
handle_actor_user_changeset(changeset)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_actor_user_changeset(changeset) do
|
||||
changeset =
|
||||
Ecto.Changeset.traverse_errors(changeset, fn
|
||||
{msg, opts} -> msg
|
||||
msg -> msg
|
||||
end)
|
||||
{:error, hd(Map.get(changeset, :email))}
|
||||
end
|
||||
|
||||
def register_bot_account(%{name: name, summary: summary}) do
|
||||
key = :public_key.generate_key({:rsa, 2048, 65_537})
|
||||
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
|
||||
@@ -466,9 +507,16 @@ defmodule Mobilizon.Actors do
|
||||
iex> get_user_by_email(user, wrong_email)
|
||||
{:error, nil}
|
||||
"""
|
||||
def get_user_by_email(email) do
|
||||
case Repo.get_by(User, email: email) do
|
||||
nil -> {:error, nil}
|
||||
def get_user_by_email(email, activated \\ nil) do
|
||||
query =
|
||||
case activated do
|
||||
nil -> from(u in User, where: u.email == ^email)
|
||||
true -> from(u in User, where: u.email == ^email and not is_nil(u.confirmed_at))
|
||||
false -> from(u in User, where: u.email == ^email and is_nil(u.confirmed_at))
|
||||
end
|
||||
|
||||
case Repo.one(query) do
|
||||
nil -> {:error, :user_not_found}
|
||||
user -> {:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,7 @@ defmodule Mobilizon.Actors.Service.Activation do
|
||||
|
||||
alias Mobilizon.{Mailer, Repo, Actors.User, Actors}
|
||||
alias Mobilizon.Email.User, as: UserEmail
|
||||
alias Mobilizon.Actors.Service.Tools
|
||||
|
||||
require Logger
|
||||
|
||||
@@ -15,7 +16,8 @@ defmodule Mobilizon.Actors.Service.Activation do
|
||||
"confirmation_sent_at" => nil,
|
||||
"confirmation_token" => nil
|
||||
}) do
|
||||
{:ok, Repo.preload(user, :actors)}
|
||||
Logger.info("User #{user.email} has been confirmed")
|
||||
{:ok, user}
|
||||
else
|
||||
_err ->
|
||||
{:error, "Invalid token"}
|
||||
@@ -23,8 +25,12 @@ defmodule Mobilizon.Actors.Service.Activation do
|
||||
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)
|
||||
with :ok <- Tools.we_can_send_email(user, :confirmation_sent_at),
|
||||
{:ok, user} <- Actors.update_user(user, %{"confirmation_sent_at" => DateTime.utc_now()}) do
|
||||
send_confirmation_email(user, locale)
|
||||
Logger.info("Sent confirmation email again to #{user.email}")
|
||||
{:ok, user.email}
|
||||
end
|
||||
end
|
||||
|
||||
def send_confirmation_email(%User{} = user, locale \\ "en") do
|
||||
|
||||
@@ -5,6 +5,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
|
||||
|
||||
alias Mobilizon.{Mailer, Repo, Actors.User}
|
||||
alias Mobilizon.Email.User, as: UserEmail
|
||||
alias Mobilizon.Actors.Service.Tools
|
||||
|
||||
@doc """
|
||||
Check that the provided token is correct and update provided password
|
||||
@@ -20,7 +21,7 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
|
||||
"reset_password_token" => nil
|
||||
})
|
||||
) do
|
||||
{:ok, Repo.preload(user, :actors)}
|
||||
{:ok, user}
|
||||
else
|
||||
err ->
|
||||
{:error, :invalid_token}
|
||||
@@ -32,11 +33,11 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
|
||||
"""
|
||||
@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),
|
||||
with :ok <- Tools.we_can_send_email(user, :reset_password_sent_at),
|
||||
{:ok, %User{} = user_updated} <-
|
||||
Repo.update(
|
||||
User.send_password_reset_changeset(user, %{
|
||||
"reset_password_token" => random_string(30),
|
||||
"reset_password_token" => Tools.random_string(30),
|
||||
"reset_password_sent_at" => DateTime.utc_now()
|
||||
})
|
||||
) do
|
||||
@@ -50,28 +51,4 @@ defmodule Mobilizon.Actors.Service.ResetPassword do
|
||||
{:error, reason} -> {:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
@spec random_string(integer) :: String.t()
|
||||
defp random_string(length) do
|
||||
length
|
||||
|> :crypto.strong_rand_bytes()
|
||||
|> 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
|
||||
|
||||
27
lib/mobilizon/actors/service/tools.ex
Normal file
27
lib/mobilizon/actors/service/tools.ex
Normal file
@@ -0,0 +1,27 @@
|
||||
defmodule Mobilizon.Actors.Service.Tools do
|
||||
alias Mobilizon.Actors.User
|
||||
|
||||
@spec we_can_send_email(User.t()) :: boolean
|
||||
def we_can_send_email(%User{} = user, key \\ :reset_password_sent_at) do
|
||||
case Map.get(user, key) do
|
||||
nil ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
case Timex.before?(Timex.shift(Map.get(user, key), hours: 1), DateTime.utc_now()) do
|
||||
true ->
|
||||
:ok
|
||||
|
||||
false ->
|
||||
{:error, :email_too_soon}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec random_string(integer) :: String.t()
|
||||
def random_string(length) do
|
||||
length
|
||||
|> :crypto.strong_rand_bytes()
|
||||
|> Base.url_encode64()
|
||||
end
|
||||
end
|
||||
@@ -12,6 +12,7 @@ defmodule Mobilizon.Actors.User do
|
||||
field(:password, :string, virtual: true)
|
||||
field(:role, :integer, default: 0)
|
||||
has_many(:actors, Actor)
|
||||
field(:default_actor_id, :integer)
|
||||
field(:confirmed_at, :utc_datetime)
|
||||
field(:confirmation_sent_at, :utc_datetime)
|
||||
field(:confirmation_token, :string)
|
||||
@@ -27,6 +28,7 @@ defmodule Mobilizon.Actors.User do
|
||||
|> cast(attrs, [
|
||||
:email,
|
||||
:role,
|
||||
:default_actor_id,
|
||||
:password_hash,
|
||||
:confirmed_at,
|
||||
:confirmation_sent_at,
|
||||
@@ -49,7 +51,8 @@ defmodule Mobilizon.Actors.User do
|
||||
struct
|
||||
|> changeset(params)
|
||||
|> cast(params, ~w(password)a, [])
|
||||
|> validate_required([:email, :password])
|
||||
|> validate_required([:email, :password, :default_actor_id])
|
||||
|> validate_email()
|
||||
|> validate_length(
|
||||
:password,
|
||||
min: 6,
|
||||
@@ -92,6 +95,19 @@ defmodule Mobilizon.Actors.User do
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_email(changeset) do
|
||||
case changeset do
|
||||
%Ecto.Changeset{valid?: true, changes: %{email: email}} ->
|
||||
case EmailChecker.valid?(email) do
|
||||
false -> add_error(changeset, :email, "Email doesn't fit required format")
|
||||
_ -> changeset
|
||||
end
|
||||
|
||||
_ ->
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
defp random_string(length) do
|
||||
length
|
||||
|> :crypto.strong_rand_bytes()
|
||||
|
||||
Reference in New Issue
Block a user