defmodule Integrate.Accounts do @moduledoc """ The Accounts context. """ import Ecto.Query, warn: false alias Ecto.Changeset alias Integrate.Repo alias Integrate.Accounts.User @doc """ Returns the list of users. ## Examples iex> list_users() [%User{}, ...] """ def list_users do Repo.all(User) end @doc """ Returns a boolean indicating whether any users exist. ## Examples iex> any_users_exist?() true """ def any_users_exist? do Repo.exists?(User) end @doc """ Gets a single user. Raises `Ecto.NoResultsError` if the User does not exist. ## Examples iex> get_user!(123) %User{} iex> get_user!(456) ** (Ecto.NoResultsError) """ def get_user!(id), do: Repo.get!(User, id) @doc """ Gets a single user. Returns `nil` if the User does not exist. ## Examples iex> get_user!(123) %User{} iex> get_user!(456) nil """ def get_user(nil), do: nil def get_user(id), do: Repo.get(User, id) @doc """ Creates a user. ## Examples iex> create_user(%{field: value}) {:ok, %User{}} iex> create_user(%{field: bad_value}) {:error, %Ecto.Changeset{}} """ def create_user(attrs \\ %{}) do %User{} |> User.changeset(attrs) |> Repo.insert() end @doc """ Updates a user. ## Examples iex> update_user(user, %{field: new_value}) {:ok, %User{}} iex> update_user(user, %{field: bad_value}) {:error, %Ecto.Changeset{}} """ def update_user(%User{} = user, attrs) do user |> User.changeset(attrs) |> Repo.update() end @doc """ Deletes a user. ## Examples iex> delete_user(user) {:ok, %User{}} iex> delete_user(user) {:error, %Ecto.Changeset{}} """ def delete_user(%User{} = user) do Repo.delete(user) end @doc """ Returns an `%Ecto.Changeset{}` for tracking user changes. ## Examples iex> change_user(user) %Ecto.Changeset{data: %User{}} """ def change_user(%User{} = user, attrs \\ %{}) do User.changeset(user, attrs) end # === Authentication === def validate_credentials(attrs) do %User{} |> User.changeset(attrs) |> Changeset.apply_action(:update) end def authenticate(%{username: username, password: password}) do User |> Repo.get_by(username: username) |> verify_password(password) end defp verify_password(%User{} = user, password) do case Argon2.verify_pass(password, user.password_hash) do true -> {:ok, user} false -> nil end end defp verify_password(nil, _) do nil end def invalid_credentials(username) do %User{} |> Changeset.cast(%{username: username}, [:username, :password]) |> Changeset.add_error(:password, "Invalid credentials") |> Changeset.apply_action(:update) end end