Packages

Easy to install Auth system with social integration just in 6 steps on Elixir Phoenix

Current section

Files

Jump to
plug_mishka_auth lib client users client_user_query.ex
Raw

lib/client/users/client_user_query.ex

defmodule MishkaAuth.Client.Users.ClientUserQuery do
import Ecto.Query
@type username() :: String.t()
@type password() :: String.t()
@type email() :: String.t()
@type data_uuid() :: Ecto.UUID.t
@type unconfirmed_email() :: email() | nil
@register_params ["name", "lastname", "username", "email", "password"]
@moduledoc """
this module helps us to handle users and connect to users database.
"""
@topic inspect(__MODULE__)
alias MishkaAuth.Helper.Db
alias MishkaAuth.Client.Users.ClientUserSchema
@doc """
this function starts push notification in this module.
"""
@spec subscribe :: :ok | {:error, any}
def subscribe do
Phoenix.PubSub.subscribe(MishkaAuth.get_config_info(:pub_sub), @topic)
end
# this function converts error which is sent by user for showing in real-time app.
defp notify_subscribers({:error, reason}, event) do
{:error, event, reason}
end
defp notify_subscribers({:ok, result}, event) do
Phoenix.PubSub.broadcast(MishkaAuth.get_config_info(:pub_sub), @topic, {__MODULE__, event, result})
Phoenix.PubSub.broadcast(MishkaAuth.get_config_info(:pub_sub), @topic <> "#{result.id}", {__MODULE__, event, result})
end
@doc """
this function accepts info which is created by map and adds user in Users table.
"""
@spec add_user(map()) ::
{:error, :add_user, Ecto.Changeset.t()} | {:ok, :add_user, Ecto.Schema.t()}
def add_user(attrs) do
add = %ClientUserSchema{}
|> ClientUserSchema.changeset(attrs)
|> Db.repo.insert()
notify_subscribers(add, [:user, :add_user])
case add do
{:ok, user_info} -> {:ok, :add_user, user_info}
{:error, changeset} -> {:error, :add_user, changeset}
end
end
@spec update_user(Ecto.Schema.t(), map()) ::
{:error, :update_user, Ecto.Changeset.t()} |
{:ok, :update_user, Ecto.Schema.t()}
defp update_user(old_data, attrs) do
update = ClientUserSchema.changeset(old_data, attrs)
|> Db.repo.update
case update do
{:ok, user_info} -> {:ok, :update_user, user_info}
{:error, changeset} -> {:error, :update_user, changeset}
end
end
@doc """
this function accepts info which is created by map and updates user in Users table.
"""
@spec edit_user(data_uuid(), map()) ::
{:error, :edit_user, :user_doesnt_exist}
| {:ok, :edit_user, Ecto.Schema.t()}
| {:error, :edit_user, :data_input_problem, Ecto.Changeset.t()}
def edit_user(id, attrs) do
with {:ok, :valid_uuid, user_id} <- MishkaAuth.Extra.valid_uuid(id),
{:ok, :find_user_with_user_id, user_info} <- find_user_with_user_id(user_id),
{:ok, :update_user, updated_user_info} <- update_user(user_info, attrs) do
notify_subscribers({:ok, updated_user_info}, [:user, :edit_user])
{:ok, :edit_user, updated_user_info}
else
{:error, :valid_uuid} ->
notify_subscribers({:error, :user_doesnt_exist}, [:user, :edit_user])
{:error, :edit_user, :user_doesnt_exist}
{:error, :update_user, changeset} ->
{:error, :edit_user, :data_input_problem, changeset}
_ ->
notify_subscribers({:error, :user_doesnt_exist}, [:user, :edit_user])
{:error, :edit_user, :user_doesnt_exist}
end
end
# convert Db.repo.delete to atom
@spec remove_user(Ecto.Schema.t()) ::
{:error, :remove_user, Ecto.Changeset.t()} | {:ok, :remove_user, Ecto.Schema.t()}
defp remove_user(user_info) do
case Db.repo.delete(user_info) do
{:ok, struct} -> {:ok, :remove_user, struct}
{:error, changeset} -> {:error, :remove_user, changeset}
end
end
@doc """
this function accepts id which was created and deletes user in Users table.
"""
@spec delete_user(data_uuid()) ::
{:error, :delete_user, :user_doesnt_exist}
| {:ok, :delete_user, Ecto.Schema.t()}
| {:error, :edit_user, :data_input_problem, Ecto.Changeset.t()}
| {:error, :delete_user, :forced_to_delete}
def delete_user(id) do
try do
with {:ok, :valid_uuid, user_id} <- MishkaAuth.Extra.valid_uuid(id),
{:ok, :find_user_with_user_id, user_info} <- find_user_with_user_id(user_id),
{:ok, :remove_user, struct} <- remove_user(user_info) do
notify_subscribers({:ok, struct}, [:user, :delete_user])
{:ok, :delete_user, struct}
else
{:error, :valid_uuid} ->
notify_subscribers({:error, :user_doesnt_exist}, [:user, :delete_user])
{:error, :delete_user, :user_doesnt_exist}
{:error, :remove_user, changeset} ->
{:error, :edit_user, :data_input_problem, changeset}
_ ->
notify_subscribers({:error, :user_doesnt_exist}, [:user, :delete_user])
{:error, :delete_user, :user_doesnt_exist}
end
rescue
_e in Ecto.ConstraintError -> {:error, :delete_user, :forced_to_delete}
end
end
@doc """
get user info with mail
"""
@spec find_user_with_email(email()) ::
{:error, :find_user_with_email} | {:ok, :find_user_with_email, Ecto.Schema.t()}
def find_user_with_email(email) do
case Db.repo.get_by(ClientUserSchema, email: "#{email}") do
nil -> {:error, :find_user_with_email}
user_info -> {:ok, :find_user_with_email, user_info}
end
end
@doc """
get user info with username
"""
@spec find_user_with_username(username()) ::
{:error, :find_user_with_username} | {:ok, :find_user_with_username, Ecto.Schema.t()}
def find_user_with_username(username) do
case Db.repo.get_by(ClientUserSchema, username: "#{username}") do
nil -> {:error, :find_user_with_username}
user_info -> {:ok, :find_user_with_username, user_info}
end
end
@doc """
get user info with id
"""
@spec find_user_with_user_id(data_uuid()) ::
{:error, :find_user_with_user_id} | {:ok, :find_user_with_user_id, Ecto.Schema.t()}
def find_user_with_user_id(id) do
case Db.repo.get(ClientUserSchema, id) do
nil -> {:error, :find_user_with_user_id}
user_info -> {:ok, :find_user_with_user_id, user_info}
end
end
@doc """
this function has two job that can check user existed and user activated. it uses email for this
"""
@spec is_user_activated?(email()) ::
{:error, :is_user_activated?, :user_not_activated | :user_not_found}
| {:ok, :is_user_activated?, Ecto.Schema.t()}
def is_user_activated?(email) do
with {:ok, :find_user_with_email, user_info} <- find_user_with_email(email),
{:ok, :is_unconfirmed_email_nil} <- is_unconfirmed_email_nil?(user_info.unconfirmed_email) do
{:ok, :is_user_activated?, user_info}
else
{:error, :find_user_with_email} -> {:error, :is_user_activated?, :user_not_found}
{:error, :is_unconfirmed_email_nil} -> {:error, :is_user_activated?, :user_not_activated}
end
end
@doc """
this function has two job that can check user existed and user activated. it uses id for this
"""
@spec is_user_activated_with_id?(data_uuid()) ::
{:error, :is_user_activated_with_id?, :user_not_activated | :user_not_found}
| {:ok, :is_user_activated_with_id, Ecto.Schema.t()}
def is_user_activated_with_id?(id) do
with {:ok, :find_user_with_user_id, user_info} <- find_user_with_user_id(id),
{:ok, :is_unconfirmed_email_nil} <- is_unconfirmed_email_nil?(user_info.unconfirmed_email) do
{:ok, :is_user_activated_with_id?, user_info}
else
{:error, :find_user_with_user_id} ->
{:error, :is_user_activated_with_id?, :user_not_found}
{:error, :is_unconfirmed_email_nil} ->
{:error, :is_user_activated_with_id?, :user_not_activated}
end
end
# convert nil or string unconfirmed_email to atom
@spec is_unconfirmed_email_nil?(unconfirmed_email()) ::
{:ok, :is_unconfirmed_email_nil}
| {:error, :is_unconfirmed_email_nil}
defp is_unconfirmed_email_nil?(unconfirmed_email) do
case unconfirmed_email do
nil -> {:ok, :is_unconfirmed_email_nil}
_ -> {:error, :is_unconfirmed_email_nil}
end
end
@spec update_user_password(Ecto.Schema.t(), map()) ::
{:error, :update_user_password, Ecto.Changeset.t()} |
{:ok, :update_user_password, Ecto.Schema.t()}
defp update_user_password(old_data, attrs) do
update = ClientUserSchema.change_password_changeset(old_data, attrs)
|> Db.repo.update
case update do
{:ok, user_update_info} -> {:ok, :update_user_password, user_update_info}
{:error, changeset} -> {:error, :update_user_password, changeset}
end
end
@spec edit_user_password(email(), map()) ::
{:error, :edit_user_password, :user_not_found}
| {:ok, :edit_user_password, Ecto.Schema.t()}
| {:error, :edit_user_password, :data_input_problem, Ecto.Changeset.t()}
@doc """
edit user password with his email
"""
def edit_user_password(email, attrs) do
with {:ok, :find_user_with_email, user_info} <- find_user_with_email(email),
{:ok, :update_user_password, user_update_info} <- update_user_password(user_info, attrs) do
{:ok, :edit_user_password, user_update_info}
else
{:error, :find_user_with_email} ->
{:error, :edit_user_password, :user_not_found}
{:error, :update_user_password, changeset} ->
{:error, :edit_user_password, :data_input_problem, changeset}
end
end
@spec edit_user_verified_email(email()) ::
{:ok, :edit_user_verified_email} | {:error, :edit_user_password, :user_not_found}
@doc """
systematic user activation
"""
def edit_user_verified_email(email) do
with {:ok, :find_user_with_email, user_info} <- find_user_with_email(email)do
Ecto.Changeset.change(user_info, %{unconfirmed_email: nil})
|> Db.repo.update
{:ok, :edit_user_verified_email}
else
{:error, :find_user_with_email} ->
{:error, :edit_user_password, :user_not_found}
end
end
@doc """
this function can help you check user password is valid or not.
"""
@spec valid_password(Ecto.Schema.t(), password()) :: {:error, :valid_password} | {:ok, :valid_password}
def valid_password(user_info, password) do
case Bcrypt.check_pass(user_info, password) do
{:ok, _params} -> {:ok, :valid_password}
_ -> {:error, :valid_password}
end
end
@spec check_password_user_and_password(username(), password(), :email | :username) ::
{:error, :check_password_user_and_password, :email | :username}
| {:ok, :check_password_user_and_password, :email | :username, Ecto.Schema.t()}
def check_password_user_and_password(username, password, :username) do
with {:ok, :find_user_with_username, user_info} <- find_user_with_username(username),
{:ok, :chack_password_not_null} <- chack_password_not_null(user_info.password_hash),
{:ok, :valid_password} <- valid_password(user_info, password) do
{:ok, :check_password_user_and_password, :username, user_info}
else
_ ->
{:error, :check_password_user_and_password, :username}
end
end
def check_password_user_and_password(email, password, :email) do
with {:ok, :find_user_with_email, user_info} <- find_user_with_email(email),
{:ok, :chack_password_not_null} <- chack_password_not_null(user_info.password_hash),
{:ok, :valid_password} <- valid_password(user_info, password) do
{:ok, :check_password_user_and_password, :email, user_info}
else
_ ->
{:error, :check_password_user_and_password, :email}
end
end
@spec chack_password_not_null(password()) ::
{:error, :chack_password_not_null} | {:ok, :chack_password_not_null}
defp chack_password_not_null(pass) do
if pass == nil, do: {:error, :chack_password_not_null}, else: {:ok, :chack_password_not_null}
end
@spec set_set_systematic_user_data(map, :direct | :social) :: map
def set_set_systematic_user_data(user_params, :direct) do
Map.merge(%{"unconfirmed_email" => user_params["email"]}, MishkaAuth.Extra.strong_params(user_params, @register_params))
end
def set_set_systematic_user_data(user_params, :social) do
Map.merge(%{"unconfirmed_email" => nil, "status" => "active"}, MishkaAuth.Extra.strong_params(user_params, @register_params))
end
def show_public_info_of_user(user_id, :user_id) do
query = from u in ClientUserSchema,
where: u.id == ^user_id,
select: %{
id: u.id,
name: u.name,
lastname: u.lastname,
username: u.username,
email: u.email
}
case Db.repo.one(query) do
nil -> {:error, :show_public_info_of_user, :user_id}
user_info -> {:ok, :show_public_info_of_user, :user_id, user_info}
end
end
def show_public_info_of_user(user_email, :email) do
query = from u in ClientUserSchema,
where: u.email == ^user_email,
select: %{
id: u.id,
name: u.name,
lastname: u.lastname,
username: u.username,
email: u.email
}
case Db.repo.one(query) do
nil -> {:error, :show_public_info_of_user, :email}
user_info -> {:ok, :show_public_info_of_user, :email, user_info}
end
end
end