| defmodule Plausible.Auth.UserSession do |
| @moduledoc """ |
| Schema for storing user session data. |
| """ |
|
|
| use Ecto.Schema |
| use Plausible |
|
|
| import Ecto.Changeset |
|
|
| alias Plausible.Auth |
|
|
| @type t() :: %__MODULE__{} |
|
|
| @rand_size 32 |
| @timeout Duration.new!(day: 14) |
|
|
| schema "user_sessions" do |
| field :token, :binary |
| field :device, :string |
| field :last_used_at, :naive_datetime |
| field :timeout_at, :naive_datetime |
|
|
| belongs_to :user, Plausible.Auth.User |
|
|
| timestamps(updated_at: false) |
| end |
|
|
| @spec timeout_duration() :: Duration.t() |
| def timeout_duration(), do: @timeout |
|
|
| @spec new_session(Auth.User.t(), String.t(), Keyword.t()) :: Ecto.Changeset.t() |
| def new_session(user, device, opts \\ []) do |
| now = Keyword.get(opts, :now, NaiveDateTime.utc_now(:second)) |
| timeout_at = Keyword.get(opts, :timeout_at, NaiveDateTime.shift(now, @timeout)) |
|
|
| %__MODULE__{} |
| |> cast(%{device: device}, [:device]) |
| |> generate_token() |
| |> put_assoc(:user, user) |
| |> put_change(:timeout_at, timeout_at) |
| |> touch_session(now) |
| end |
|
|
| @spec touch_session(t() | Ecto.Changeset.t(), NaiveDateTime.t()) :: Ecto.Changeset.t() |
| def touch_session(session, now \\ NaiveDateTime.utc_now(:second)) do |
| changeset = change(session) |
|
|
| on_ee do |
| case get_field(changeset, :user) do |
| %{type: :sso} -> |
| put_change(changeset, :last_used_at, now) |
|
|
| _ -> |
| changeset |
| |> put_change(:last_used_at, now) |
| |> put_change(:timeout_at, NaiveDateTime.shift(now, @timeout)) |
| end |
| else |
| changeset |
| |> put_change(:last_used_at, now) |
| |> put_change(:timeout_at, NaiveDateTime.shift(now, @timeout)) |
| end |
| end |
|
|
| defp generate_token(changeset) do |
| token = :crypto.strong_rand_bytes(@rand_size) |
| put_change(changeset, :token, token) |
| end |
| end |
|
|