1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.UserInviteToken do
11 alias Pleroma.UserInviteToken
13 @type token :: String.t()
15 schema "user_invite_tokens" do
16 field(:token, :string)
17 field(:used, :boolean, default: false)
18 field(:max_use, :integer)
19 field(:expire_at, :date)
20 field(:uses, :integer)
26 def create_token(options \\ []) do
27 token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
29 max_use = options[:max_use]
30 expire_at = options[:expire_at]
46 query = from(u in UserInviteToken, order_by: u.id)
50 def used_changeset(struct) do
53 |> put_change(:used, true)
56 @spec mark_as_used(token()) :: {:ok, UserInviteToken.t()} | {:error, token()}
57 def mark_as_used(token) do
58 with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}),
59 {:ok, token} <- Repo.update(used_changeset(token)) do
66 defp token_type(%{expire_at: nil, max_use: nil} = token), do: %{token | token_type: "one_time"}
68 defp token_type(%{expire_at: _expire_at, max_use: nil} = token),
69 do: %{token | token_type: "date_limited"}
71 defp token_type(%{expire_at: nil, max_use: _max_use} = token),
72 do: %{token | token_type: "reusable"}
74 defp token_type(%{expire_at: _expire_at, max_use: _max_use} = token),
75 do: %{token | token_type: "reusable_date_limited"}
77 @spec valid_token?(UserInviteToken.t()) :: boolean()
78 def valid_token?(%{token_type: "one_time"} = token) do
82 def valid_token?(%{token_type: "date_limited"} = token) do
83 not_overdue_date?(token) and not token.used
86 def valid_token?(%{token_type: "reusable"} = token) do
87 token.uses < token.max_use and not token.used
90 def valid_token?(%{token_type: "reusable_date_limited"} = token) do
91 not_overdue_date?(token) and token.uses < token.max_use and not token.used
94 defp not_overdue_date?(%{expire_at: expire_at} = token) do
95 Date.compare(Date.utc_today(), expire_at) in [:lt, :eq] ||
96 (Repo.update!(change(token, used: true)) && false)
99 def update_usage(%{token_type: "date_limited"}), do: nil
101 def update_usage(%{token_type: "one_time"} = token) do
102 UserInviteToken.mark_as_used(token.token)
105 def update_usage(%{token_type: token_type} = token)
106 when token_type == "reusable" or token_type == "reusable_date_limited" do
107 new_uses = token.uses + 1
114 if new_uses >= token.max_use do
115 Map.put(changes, :used, true)
120 change(token, changes) |> Repo.update!()