Current section

Files

Jump to
webauthn_components templates contexts identity.ex
Raw

templates/contexts/identity.ex

defmodule <%= inspect @app_pascal_case %>.Identity do
@moduledoc """
Functions for managing users.
"""
alias Ecto.Changeset
alias <%= inspect @app_pascal_case %>.Repo
alias <%= inspect @app_pascal_case %>.Identity.User
alias <%= inspect @app_pascal_case %>.Identity.UserToken
import Ecto.Query
@token_expiration {24, :hour}
@doc """
Returns all `User` records with optional preloads.
"""
@spec list(preloads :: list()) :: [User.t()]
def list(preloads \\ []) when is_list(preloads) do
User
|> Repo.all()
|> Repo.preload(preloads)
end
@doc """
Inserts a new `User` into the repo.
"""
@spec create(attrs :: map()) :: {:ok, User.t()} | {:error, Changeset.t()}
def create(attrs) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
@doc """
Retrieves a `User` from the repo.
"""
@spec get(id :: binary(), preloads :: list()) :: {:ok, User.t()} | {:error, :not_found}
def get(id, preloads \\ []) do
result =
User
|> Repo.get(id)
|> Repo.preload(preloads)
case result do
%User{} ->
{:ok, result}
nil ->
{:error, :not_found}
end
end
defp get_expiration_timestamp do
{expiration, unit} = @token_expiration
NaiveDateTime.utc_now()
|> NaiveDateTime.add(-expiration, unit)
end
@doc """
Retrieves a `User` by querying a valid token.
"""
@spec get_by_token(raw_value :: binary(), type :: atom()) ::
{:ok, User.t()} | {:error, :not_found}
def get_by_token(raw_value, type \\ :session) when is_binary(raw_value) do
expiration_timestamp = get_expiration_timestamp()
query =
from(user in User,
join: token in assoc(user, :tokens),
where:
token.value == ^raw_value and
token.type == ^type and
token.inserted_at > ^expiration_timestamp,
order_by: [desc: token.inserted_at],
limit: 1
)
case Repo.one(query) do
%User{} = user ->
{:ok, user}
nil ->
{:error, :not_found}
end
end
@doc """
Deletes a single `UserToken`.
"""
@spec delete_token(token :: UserToken.t()) :: {:ok, UserToken.t()} | {:error, Changeset.t()}
def delete_token(%UserToken{} = token) do
Repo.delete(token)
end
@doc """
Deletes all `UserToken`s which have expired.
"""
@spec delete_all_expired_tokens :: {pos_integer(), term()}
def delete_all_expired_tokens do
expiration_timestamp = get_expiration_timestamp()
UserToken
|> where([t], t.inserted_at <= ^expiration_timestamp)
|> Repo.delete_all()
end
@doc """
Retrieves a `User` by querying an associated `:key_id`.
"""
@spec get_by_key_id(key_id :: binary()) :: {:ok, User.t()} | {:error, :not_found}
def get_by_key_id(key_id) when is_binary(key_id) do
query =
from(user in User,
join: key in assoc(user, :keys),
preload: [:keys],
where: key.key_id == ^key_id
)
case Repo.one(query) do
%User{} = user ->
{:ok, user}
nil ->
{:error, :not_found}
end
end
@doc """
Modifies an existing `User` in the repo.
"""
@spec update(user :: User.t(), attrs :: map()) :: {:ok, User.t()} | {:error, Changeset.t()}
def update(%User{} = user, attrs) do
user
|> User.changeset(attrs)
|> Repo.update()
end
@doc """
Removes an existing `User` from the repo.
"""
@spec delete(user :: User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
def delete(%User{} = user) do
Repo.delete(user)
end
# USER TOKENS
@doc """
Inserts a new `UserToken` into the repo.
"""
@spec create_token(attrs :: map()) :: {:ok, UserToken.t()} | {:error, Changeset.t()}
def create_token(attrs) do
%UserToken{}
|> UserToken.changeset(attrs)
|> Repo.insert()
end
@doc """
Deletes all session tokens belonging to a user.
"""
def delete_all_user_sessions(user_id) do
from(token in UserToken, where: token.user_id == ^user_id and token.type == :session)
|> Repo.delete_all()
end
end