Refactoring of Users context
This commit is contained in:
@@ -1,63 +1,80 @@
|
||||
import EctoEnum
|
||||
|
||||
defenum(Mobilizon.Users.UserRoleEnum, :user_role_type, [
|
||||
:administrator,
|
||||
:moderator,
|
||||
:user
|
||||
])
|
||||
|
||||
defmodule Mobilizon.Users.User do
|
||||
@moduledoc """
|
||||
Represents a local user
|
||||
Represents a local user.
|
||||
"""
|
||||
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Users.User
|
||||
alias Mobilizon.Service.EmailChecker
|
||||
alias Mobilizon.Crypto
|
||||
alias Mobilizon.Events.FeedToken
|
||||
alias Mobilizon.Service.EmailChecker
|
||||
alias Mobilizon.Users.{User, UserRole}
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
email: String.t(),
|
||||
password_hash: String.t(),
|
||||
password: String.t(),
|
||||
role: UserRole.t(),
|
||||
confirmed_at: DateTime.t(),
|
||||
confirmation_sent_at: DateTime.t(),
|
||||
confirmation_token: String.t(),
|
||||
reset_password_sent_at: DateTime.t(),
|
||||
reset_password_token: String.t(),
|
||||
default_actor: Actor.t(),
|
||||
actors: [Actor.t()],
|
||||
feed_tokens: [FeedToken.t()]
|
||||
}
|
||||
|
||||
@required_attrs [:email]
|
||||
@optional_attrs [
|
||||
:role,
|
||||
:password,
|
||||
:password_hash,
|
||||
:confirmed_at,
|
||||
:confirmation_sent_at,
|
||||
:confirmation_token,
|
||||
:reset_password_sent_at,
|
||||
:reset_password_token
|
||||
]
|
||||
@attrs @required_attrs ++ @optional_attrs
|
||||
|
||||
@registration_required_attrs [:email, :password]
|
||||
|
||||
@password_reset_required_attrs [:password, :reset_password_token, :reset_password_sent_at]
|
||||
|
||||
@confirmation_token_length 30
|
||||
|
||||
schema "users" do
|
||||
field(:email, :string)
|
||||
field(:password_hash, :string)
|
||||
field(:password, :string, virtual: true)
|
||||
field(:role, Mobilizon.Users.UserRoleEnum, default: :user)
|
||||
has_many(:actors, Actor)
|
||||
belongs_to(:default_actor, Actor)
|
||||
field(:role, UserRole, default: :user)
|
||||
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)
|
||||
|
||||
belongs_to(:default_actor, Actor)
|
||||
has_many(:actors, Actor)
|
||||
has_many(:feed_tokens, FeedToken, foreign_key: :user_id)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec changeset(t | Ecto.Changeset.t(), map) :: Ecto.Changeset.t()
|
||||
def changeset(%User{} = user, attrs) do
|
||||
changeset =
|
||||
user
|
||||
|> cast(attrs, [
|
||||
:email,
|
||||
:role,
|
||||
:password,
|
||||
:password_hash,
|
||||
:confirmed_at,
|
||||
:confirmation_sent_at,
|
||||
:confirmation_token,
|
||||
:reset_password_sent_at,
|
||||
:reset_password_token
|
||||
])
|
||||
|> validate_required([:email])
|
||||
|> cast(attrs, @attrs)
|
||||
|> validate_required(@required_attrs)
|
||||
|> unique_constraint(:email, message: "This email is already used.")
|
||||
|> validate_email()
|
||||
|> validate_length(
|
||||
:password,
|
||||
min: 6,
|
||||
max: 100,
|
||||
message: "The chosen password is too short."
|
||||
)
|
||||
|> validate_length(:password, min: 6, max: 100, message: "The chosen password is too short.")
|
||||
|
||||
if Map.has_key?(attrs, :default_actor) do
|
||||
put_assoc(changeset, :default_actor, attrs.default_actor)
|
||||
@@ -66,11 +83,13 @@ defmodule Mobilizon.Users.User do
|
||||
end
|
||||
end
|
||||
|
||||
def registration_changeset(struct, params) do
|
||||
struct
|
||||
|> changeset(params)
|
||||
@doc false
|
||||
@spec registration_changeset(User.t(), map) :: Ecto.Changeset.t()
|
||||
def registration_changeset(%User{} = user, attrs) do
|
||||
user
|
||||
|> changeset(attrs)
|
||||
|> cast_assoc(:default_actor)
|
||||
|> validate_required([:email, :password])
|
||||
|> validate_required(@registration_required_attrs)
|
||||
|> hash_password()
|
||||
|> save_confirmation_token()
|
||||
|> unique_constraint(
|
||||
@@ -79,16 +98,18 @@ defmodule Mobilizon.Users.User do
|
||||
)
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec send_password_reset_changeset(User.t(), map) :: Ecto.Changeset.t()
|
||||
def send_password_reset_changeset(%User{} = user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:reset_password_token, :reset_password_sent_at])
|
||||
cast(user, attrs, [:reset_password_token, :reset_password_sent_at])
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec password_reset_changeset(User.t(), map) :: Ecto.Changeset.t()
|
||||
def password_reset_changeset(%User{} = user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:password, :reset_password_token, :reset_password_sent_at])
|
||||
|> validate_length(
|
||||
:password,
|
||||
|> cast(attrs, @password_reset_required_attrs)
|
||||
|> validate_length(:password,
|
||||
min: 6,
|
||||
max: 100,
|
||||
message: "registration.error.password_too_short"
|
||||
@@ -96,28 +117,45 @@ defmodule Mobilizon.Users.User do
|
||||
|> hash_password()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks whether an user is confirmed.
|
||||
"""
|
||||
@spec is_confirmed(User.t()) :: boolean
|
||||
def is_confirmed(%User{confirmed_at: nil}), do: false
|
||||
def is_confirmed(%User{}), do: true
|
||||
|
||||
@doc """
|
||||
Returns whether an user owns an actor.
|
||||
"""
|
||||
@spec owns_actor(User.t(), integer | String.t()) :: {:is_owned, Actor.t() | nil}
|
||||
def owns_actor(%User{actors: actors}, actor_id) do
|
||||
user_actor = Enum.find(actors, fn actor -> "#{actor.id}" == "#{actor_id}" end)
|
||||
|
||||
{:is_owned, user_actor}
|
||||
end
|
||||
|
||||
@spec save_confirmation_token(Ecto.Changeset.t()) :: Ecto.Changeset.t()
|
||||
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))
|
||||
now = DateTime.utc_now()
|
||||
|
||||
put_change(
|
||||
changeset,
|
||||
:confirmation_sent_at,
|
||||
DateTime.utc_now() |> DateTime.truncate(:second)
|
||||
)
|
||||
changeset
|
||||
|> put_change(:confirmation_token, Crypto.random_string(@confirmation_token_length))
|
||||
|> put_change(:confirmation_sent_at, DateTime.truncate(now, :second))
|
||||
|
||||
_ ->
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
@spec validate_email(Ecto.Changeset.t()) :: Ecto.Changeset.t()
|
||||
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
|
||||
true -> changeset
|
||||
end
|
||||
|
||||
_ ->
|
||||
@@ -125,46 +163,14 @@ defmodule Mobilizon.Users.User do
|
||||
end
|
||||
end
|
||||
|
||||
defp random_string(length) do
|
||||
length
|
||||
|> :crypto.strong_rand_bytes()
|
||||
|> Base.url_encode64()
|
||||
end
|
||||
|
||||
# Hash password when it's changed
|
||||
@spec hash_password(Ecto.Changeset.t()) :: Ecto.Changeset.t()
|
||||
defp hash_password(changeset) do
|
||||
case changeset do
|
||||
%Ecto.Changeset{valid?: true, changes: %{password: password}} ->
|
||||
put_change(
|
||||
changeset,
|
||||
:password_hash,
|
||||
Argon2.hash_pwd_salt(password)
|
||||
)
|
||||
put_change(changeset, :password_hash, Argon2.hash_pwd_salt(password))
|
||||
|
||||
_ ->
|
||||
changeset
|
||||
end
|
||||
end
|
||||
|
||||
def is_confirmed(%User{confirmed_at: nil} = _user), do: {:error, :unconfirmed}
|
||||
def is_confirmed(%User{} = user), do: {:ok, user}
|
||||
|
||||
@doc """
|
||||
Returns whether an user owns an actor
|
||||
"""
|
||||
@spec owns_actor(struct(), String.t()) :: {:is_owned, false} | {:is_owned, true, Actor.t()}
|
||||
def owns_actor(%User{} = user, actor_id) when is_binary(actor_id) do
|
||||
case Integer.parse(actor_id) do
|
||||
{actor_id, ""} -> owns_actor(user, actor_id)
|
||||
_ -> {:is_owned, false}
|
||||
end
|
||||
end
|
||||
|
||||
@spec owns_actor(struct(), integer()) :: {:is_owned, false} | {:is_owned, true, Actor.t()}
|
||||
def owns_actor(%User{actors: actors}, actor_id) do
|
||||
case Enum.find(actors, fn a -> a.id == actor_id end) do
|
||||
nil -> {:is_owned, false}
|
||||
actor -> {:is_owned, true, actor}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,69 +3,60 @@ defmodule Mobilizon.Users do
|
||||
The Users context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
import Ecto.Query
|
||||
import EctoEnum
|
||||
|
||||
alias Mobilizon.Repo
|
||||
import Mobilizon.Ecto
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events
|
||||
alias Mobilizon.{Page, Repo}
|
||||
alias Mobilizon.Users.User
|
||||
|
||||
@doc false
|
||||
def data() do
|
||||
Dataloader.Ecto.new(Repo, query: &query/2)
|
||||
end
|
||||
@type tokens :: %{
|
||||
required(:access_token) => String.t(),
|
||||
required(:refresh_token) => String.t()
|
||||
}
|
||||
|
||||
defenum(UserRole, :user_role, [:administrator, :moderator, :user])
|
||||
|
||||
@doc false
|
||||
def query(queryable, _params) do
|
||||
queryable
|
||||
end
|
||||
@spec data :: Dataloader.Ecto.t()
|
||||
def data, do: Dataloader.Ecto.new(Mobilizon.Repo, query: &query/2)
|
||||
|
||||
@doc false
|
||||
@spec query(Ecto.Query.t(), map) :: Ecto.Query.t()
|
||||
def query(queryable, _params), do: queryable
|
||||
|
||||
@doc """
|
||||
Register user
|
||||
Registers an user.
|
||||
"""
|
||||
@spec register(map()) :: {:ok, User.t()} | {:error, String.t()}
|
||||
@spec register(map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def register(%{email: _email, password: _password} = args) do
|
||||
with {:ok, %User{} = user} <-
|
||||
%User{}
|
||||
|> User.registration_changeset(args)
|
||||
|> Mobilizon.Repo.insert() do
|
||||
Mobilizon.Events.create_feed_token(%{"user_id" => user.id})
|
||||
|> Repo.insert() do
|
||||
Events.create_feed_token(%{"user_id" => user.id})
|
||||
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets an user by it's email
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_user_by_email("test@test.tld", true)
|
||||
{:ok, %Mobilizon.Users.User{}}
|
||||
|
||||
iex> get_user_by_email("test@notfound.tld", false)
|
||||
{:error, :user_not_found}
|
||||
Gets a single user.
|
||||
Raises `Ecto.NoResultsError` if the user does not exist.
|
||||
"""
|
||||
@spec get_user!(integer | String.t()) :: User.t()
|
||||
def get_user!(id), do: Repo.get!(User, id)
|
||||
|
||||
@doc """
|
||||
Gets an user by its email.
|
||||
"""
|
||||
@spec get_user_by_email(String.t(), boolean | nil) ::
|
||||
{:ok, User.t()} | {:error, :user_not_found}
|
||||
def get_user_by_email(email, activated \\ nil) do
|
||||
query =
|
||||
case activated do
|
||||
nil ->
|
||||
from(u in User, where: u.email == ^email, preload: :default_actor)
|
||||
|
||||
true ->
|
||||
from(
|
||||
u in User,
|
||||
where: u.email == ^email and not is_nil(u.confirmed_at),
|
||||
preload: :default_actor
|
||||
)
|
||||
|
||||
false ->
|
||||
from(
|
||||
u in User,
|
||||
where: u.email == ^email and is_nil(u.confirmed_at),
|
||||
preload: :default_actor
|
||||
)
|
||||
end
|
||||
query = user_by_email_query(email, activated)
|
||||
|
||||
case Repo.one(query) do
|
||||
nil -> {:error, :user_not_found}
|
||||
@@ -74,45 +65,29 @@ defmodule Mobilizon.Users do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get an user by it's activation token
|
||||
Get an user by its activation token.
|
||||
"""
|
||||
@spec get_user_by_activation_token(String.t()) :: Actor.t()
|
||||
@spec get_user_by_activation_token(String.t()) :: Actor.t() | nil
|
||||
def get_user_by_activation_token(token) do
|
||||
Repo.one(
|
||||
from(
|
||||
u in User,
|
||||
where: u.confirmation_token == ^token,
|
||||
preload: [:default_actor]
|
||||
)
|
||||
)
|
||||
token
|
||||
|> user_by_activation_token_query()
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get an user by it's reset password token
|
||||
Get an user by its reset password token.
|
||||
"""
|
||||
@spec get_user_by_reset_password_token(String.t()) :: Actor.t()
|
||||
def get_user_by_reset_password_token(token) do
|
||||
Repo.one(
|
||||
from(
|
||||
u in User,
|
||||
where: u.reset_password_token == ^token,
|
||||
preload: [:default_actor]
|
||||
)
|
||||
)
|
||||
token
|
||||
|> user_by_reset_password_token_query()
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a user.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_user(User{}, %{password: "coucou"})
|
||||
{:ok, %Mobilizon.Users.User{}}
|
||||
|
||||
iex> update_user(User{}, %{password: nil})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
Updates an user.
|
||||
"""
|
||||
@spec update_user(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_user(%User{} = user, attrs) do
|
||||
with {:ok, %User{} = user} <-
|
||||
user
|
||||
@@ -123,65 +98,28 @@ defmodule Mobilizon.Users do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a User.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_user(%User{email: "test@test.tld"})
|
||||
{:ok, %Mobilizon.Users.User{}}
|
||||
|
||||
iex> delete_user(%User{})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
Deletes an user.
|
||||
"""
|
||||
@spec delete_user(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def delete_user(%User{} = user) do
|
||||
Repo.delete(user)
|
||||
end
|
||||
|
||||
# @doc """
|
||||
# Returns an `%Ecto.Changeset{}` for tracking user changes.
|
||||
|
||||
# ## Examples
|
||||
|
||||
# iex> change_user(%Mobilizon.Users.User{})
|
||||
# %Ecto.Changeset{data: %Mobilizon.Users.User{}}
|
||||
|
||||
# """
|
||||
# def change_user(%User{} = user) do
|
||||
# User.changeset(user, %{})
|
||||
# end
|
||||
|
||||
@doc """
|
||||
Gets a single user.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the User does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_user!(123)
|
||||
%Mobilizon.Users.User{}
|
||||
|
||||
iex> get_user!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
Get an user with its actors
|
||||
Raises `Ecto.NoResultsError` if the user does not exist.
|
||||
"""
|
||||
def get_user!(id), do: Repo.get!(User, id)
|
||||
|
||||
@doc """
|
||||
Get an user with it's actors
|
||||
|
||||
Raises `Ecto.NoResultsError` if the User does not exist.
|
||||
"""
|
||||
@spec get_user_with_actors!(integer()) :: User.t()
|
||||
@spec get_user_with_actors!(integer | String.t()) :: User.t()
|
||||
def get_user_with_actors!(id) do
|
||||
user = Repo.get!(User, id)
|
||||
Repo.preload(user, [:actors, :default_actor])
|
||||
id
|
||||
|> get_user!()
|
||||
|> Repo.preload([:actors, :default_actor])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get user with it's actors by ID
|
||||
Get user with its actors.
|
||||
"""
|
||||
@spec get_user_with_actors(integer()) :: User.t()
|
||||
@spec get_user_with_actors(integer()) :: {:ok, User.t()} | {:error, String.t()}
|
||||
def get_user_with_actors(id) do
|
||||
case Repo.get(User, id) do
|
||||
nil ->
|
||||
@@ -198,21 +136,19 @@ defmodule Mobilizon.Users do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the associated actor for an user, either the default set one or the first found
|
||||
Gets the associated actor for an user, either the default set one or the first
|
||||
found.
|
||||
"""
|
||||
@spec get_actor_for_user(Mobilizon.Users.User.t()) :: Mobilizon.Actors.Actor.t()
|
||||
def get_actor_for_user(%Mobilizon.Users.User{} = user) do
|
||||
case Repo.one(
|
||||
from(
|
||||
a in Actor,
|
||||
join: u in User,
|
||||
on: u.default_actor_id == a.id,
|
||||
where: u.id == ^user.id
|
||||
)
|
||||
) do
|
||||
@spec get_actor_for_user(User.t()) :: Actor.t() | nil
|
||||
def get_actor_for_user(%User{} = user) do
|
||||
actor =
|
||||
user
|
||||
|> actor_for_user_query()
|
||||
|> Repo.one()
|
||||
|
||||
case actor do
|
||||
nil ->
|
||||
case user
|
||||
|> get_actors_for_user() do
|
||||
case get_actors_for_user(user) do
|
||||
[] -> nil
|
||||
actors -> hd(actors)
|
||||
end
|
||||
@@ -222,94 +158,48 @@ defmodule Mobilizon.Users do
|
||||
end
|
||||
end
|
||||
|
||||
def get_actors_for_user(%User{id: user_id}) do
|
||||
Repo.all(from(a in Actor, where: a.user_id == ^user_id))
|
||||
@doc """
|
||||
Gets actors for an user.
|
||||
"""
|
||||
@spec get_actors_for_user(User.t()) :: [Actor.t()]
|
||||
def get_actors_for_user(%User{} = user) do
|
||||
user
|
||||
|> actors_for_user_query()
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Authenticate user
|
||||
Updates user's default actor.
|
||||
Raises `Ecto.NoResultsError` if the user does not exist.
|
||||
"""
|
||||
def authenticate(%{user: user, password: password}) do
|
||||
# Does password match the one stored in the database?
|
||||
with true <- Argon2.verify_pass(password, user.password_hash),
|
||||
# Yes, create and return the token
|
||||
{:ok, tokens} <- generate_tokens(user) do
|
||||
{:ok, tokens}
|
||||
else
|
||||
_ ->
|
||||
# No, return an error
|
||||
{:error, :unauthorized}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generate access token and refresh token
|
||||
"""
|
||||
def generate_tokens(user) do
|
||||
with {:ok, access_token} <- generate_access_token(user),
|
||||
{:ok, refresh_token} <- generate_refresh_token(user) do
|
||||
{:ok, %{access_token: access_token, refresh_token: refresh_token}}
|
||||
end
|
||||
end
|
||||
|
||||
defp generate_access_token(user) do
|
||||
with {:ok, access_token, _claims} <-
|
||||
MobilizonWeb.Guardian.encode_and_sign(user, %{}, token_type: "access") do
|
||||
{:ok, access_token}
|
||||
end
|
||||
end
|
||||
|
||||
def generate_refresh_token(user) do
|
||||
with {:ok, refresh_token, _claims} <-
|
||||
MobilizonWeb.Guardian.encode_and_sign(user, %{}, token_type: "refresh") do
|
||||
{:ok, refresh_token}
|
||||
end
|
||||
end
|
||||
|
||||
@spec update_user_default_actor(integer | String.t(), integer | String.t()) :: User.t()
|
||||
def update_user_default_actor(user_id, actor_id) do
|
||||
with _ <-
|
||||
from(
|
||||
u in User,
|
||||
where: u.id == ^user_id,
|
||||
update: [
|
||||
set: [
|
||||
default_actor_id: ^actor_id
|
||||
]
|
||||
]
|
||||
)
|
||||
user_id
|
||||
|> update_user_default_actor_query(actor_id)
|
||||
|> Repo.update_all([]) do
|
||||
Repo.get!(User, user_id)
|
||||
user_id
|
||||
|> get_user!()
|
||||
|> Repo.preload([:default_actor])
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list of users.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_users()
|
||||
[%Mobilizon.Users.User{}]
|
||||
|
||||
"""
|
||||
@spec list_users(integer | nil, integer | nil, atom | nil, atom | nil) :: [User.t()]
|
||||
def list_users(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
|
||||
Repo.all(
|
||||
User
|
||||
|> paginate(page, limit)
|
||||
|> sort(sort, direction)
|
||||
)
|
||||
User
|
||||
|> Page.paginate(page, limit)
|
||||
|> sort(sort, direction)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list of administrators.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_admins()
|
||||
[%Mobilizon.Users.User{role: :administrator}]
|
||||
|
||||
"""
|
||||
def list_admins() do
|
||||
@spec list_admins :: [User.t()]
|
||||
def list_admins do
|
||||
User
|
||||
|> where([u], u.role == ^:administrator)
|
||||
|> Repo.all()
|
||||
@@ -317,25 +207,129 @@ defmodule Mobilizon.Users do
|
||||
|
||||
@doc """
|
||||
Returns the list of moderators.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_moderators()
|
||||
[%Mobilizon.Users.User{role: :moderator}, %Mobilizon.Users.User{role: :administrator}]
|
||||
|
||||
"""
|
||||
def list_moderators() do
|
||||
@spec list_moderators :: [User.t()]
|
||||
def list_moderators do
|
||||
User
|
||||
|> where([u], u.role in ^[:administrator, :moderator])
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
def count_users() do
|
||||
Repo.one(
|
||||
from(
|
||||
u in User,
|
||||
select: count(u.id)
|
||||
)
|
||||
@doc """
|
||||
Counts users.
|
||||
"""
|
||||
@spec count_users :: integer
|
||||
def count_users do
|
||||
Repo.one(from(u in User, select: count(u.id)))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Authenticate an user.
|
||||
"""
|
||||
@spec authenticate(User.t()) :: {:ok, tokens} | {:error, :unauthorized}
|
||||
def authenticate(%{user: %User{password_hash: password_hash} = user, password: password}) do
|
||||
# Does password match the one stored in the database?
|
||||
if Argon2.verify_pass(password, password_hash) do
|
||||
{:ok, _tokens} = generate_tokens(user)
|
||||
else
|
||||
{:error, :unauthorized}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generates access token and refresh token for an user.
|
||||
"""
|
||||
@spec generate_tokens(User.t()) :: {:ok, tokens}
|
||||
def generate_tokens(user) do
|
||||
with {:ok, access_token} <- generate_access_token(user),
|
||||
{:ok, refresh_token} <- generate_refresh_token(user) do
|
||||
{:ok, %{access_token: access_token, refresh_token: refresh_token}}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generates access token for an user.
|
||||
"""
|
||||
@spec generate_access_token(User.t()) :: {:ok, String.t()}
|
||||
def generate_access_token(user) do
|
||||
with {:ok, access_token, _claims} <-
|
||||
MobilizonWeb.Guardian.encode_and_sign(user, %{}, token_type: "access") do
|
||||
{:ok, access_token}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generates refresh token for an user.
|
||||
"""
|
||||
@spec generate_refresh_token(User.t()) :: {:ok, String.t()}
|
||||
def generate_refresh_token(user) do
|
||||
with {:ok, refresh_token, _claims} <-
|
||||
MobilizonWeb.Guardian.encode_and_sign(user, %{}, token_type: "refresh") do
|
||||
{:ok, refresh_token}
|
||||
end
|
||||
end
|
||||
|
||||
@spec user_by_email_query(String.t(), boolean | nil) :: Ecto.Query.t()
|
||||
defp user_by_email_query(email, nil) do
|
||||
from(u in User, where: u.email == ^email, preload: :default_actor)
|
||||
end
|
||||
|
||||
defp user_by_email_query(email, true) do
|
||||
from(
|
||||
u in User,
|
||||
where: u.email == ^email and not is_nil(u.confirmed_at),
|
||||
preload: :default_actor
|
||||
)
|
||||
end
|
||||
|
||||
defp user_by_email_query(email, false) do
|
||||
from(
|
||||
u in User,
|
||||
where: u.email == ^email and is_nil(u.confirmed_at),
|
||||
preload: :default_actor
|
||||
)
|
||||
end
|
||||
|
||||
@spec user_by_activation_token_query(String.t()) :: Ecto.Query.t()
|
||||
defp user_by_activation_token_query(token) do
|
||||
from(
|
||||
u in User,
|
||||
where: u.confirmation_token == ^token,
|
||||
preload: [:default_actor]
|
||||
)
|
||||
end
|
||||
|
||||
@spec user_by_reset_password_token_query(String.t()) :: Ecto.Query.t()
|
||||
defp user_by_reset_password_token_query(token) do
|
||||
from(
|
||||
u in User,
|
||||
where: u.reset_password_token == ^token,
|
||||
preload: [:default_actor]
|
||||
)
|
||||
end
|
||||
|
||||
@spec actor_for_user_query(User.t()) :: Ecto.Query.t()
|
||||
defp actor_for_user_query(%User{id: user_id}) do
|
||||
from(
|
||||
a in Actor,
|
||||
join: u in User,
|
||||
on: u.default_actor_id == a.id,
|
||||
where: u.id == ^user_id
|
||||
)
|
||||
end
|
||||
|
||||
@spec actors_for_user_query(User.t()) :: Ecto.Query.t()
|
||||
defp actors_for_user_query(%User{id: user_id}) do
|
||||
from(a in Actor, where: a.user_id == ^user_id)
|
||||
end
|
||||
|
||||
@spec update_user_default_actor_query(integer | String.t(), integer | String.t()) ::
|
||||
Ecto.Query.t()
|
||||
defp update_user_default_actor_query(user_id, actor_id) do
|
||||
from(
|
||||
u in User,
|
||||
where: u.id == ^user_id,
|
||||
update: [set: [default_actor_id: ^actor_id]]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user