75 lines
2.2 KiB
Elixir
75 lines
2.2 KiB
Elixir
defmodule Plausible.Plugins.API.Tokens do
|
|
@moduledoc """
|
|
Context module for Plugins API Tokens.
|
|
Exposes high-level operation for token-based authentication flows.
|
|
"""
|
|
alias Plausible.Plugins.API.Token
|
|
alias Plausible.Site
|
|
alias Plausible.Repo
|
|
|
|
import Ecto.Query
|
|
|
|
@spec create(Site.t(), String.t()) ::
|
|
{:ok, Token.t(), String.t()} | {:error, Ecto.Changeset.t()}
|
|
def create(%Site{} = site, description, generated_token \\ Token.generate()) do
|
|
with changeset <- Token.insert_changeset(site, generated_token, %{description: description}),
|
|
{:ok, saved_token} <- Repo.insert(changeset) do
|
|
{:ok, saved_token, generated_token.raw}
|
|
end
|
|
end
|
|
|
|
@spec find(String.t()) :: {:ok, Token.t()} | {:error, :not_found}
|
|
def find(raw) do
|
|
found =
|
|
Repo.one(
|
|
from(t in Token,
|
|
inner_join: s in Site,
|
|
on: s.id == t.site_id,
|
|
where: t.token_hash == ^Token.hash(raw),
|
|
preload: [:site]
|
|
)
|
|
)
|
|
|
|
if found do
|
|
{:ok, found}
|
|
else
|
|
{:error, :not_found}
|
|
end
|
|
end
|
|
|
|
@spec delete(Site.t(), String.t()) :: :ok
|
|
def delete(site, token_id) do
|
|
Repo.delete_all(from(t in Token, where: t.site_id == ^site.id and t.id == ^token_id))
|
|
:ok
|
|
end
|
|
|
|
@spec list(Site.t()) :: {:ok, [Token.t()]}
|
|
def list(site) do
|
|
Repo.all(
|
|
from(t in Token, where: t.site_id == ^site.id, order_by: [desc: t.inserted_at, desc: t.id])
|
|
)
|
|
end
|
|
|
|
@spec any?(Site.t()) :: boolean()
|
|
def any?(site) do
|
|
Repo.exists?(from(t in Token, where: t.site_id == ^site.id))
|
|
end
|
|
|
|
@spec update_last_seen(Token.t(), NaiveDateTime.t()) :: {:ok, Token.t()}
|
|
def update_last_seen(token, now \\ NaiveDateTime.utc_now()) do
|
|
# we don't need very precise timestamp tracking, and to spare postgres we only
|
|
# update that timestamp in 5m windows - this is mostly to help users reason
|
|
# about what token they have, in case of rotations
|
|
now = NaiveDateTime.truncate(now, :second)
|
|
last_used = token.last_used_at
|
|
|
|
if is_nil(last_used) or NaiveDateTime.diff(now, last_used, :minute) > 5 do
|
|
token
|
|
|> Ecto.Changeset.change(%{last_used_at: now})
|
|
|> Repo.update()
|
|
else
|
|
{:ok, token}
|
|
end
|
|
end
|
|
end
|