token -> invite renaming

This commit is contained in:
Alex S 2019-04-06 20:24:22 +07:00
parent be54e40890
commit 47b07cec49
6 changed files with 124 additions and 141 deletions

View file

@ -315,19 +315,19 @@ def run(["invite" | rest]) do
end end
options = Keyword.put(options, :expire_at, expire_at) options = Keyword.put(options, :expire_at, expire_at)
options = Enum.into(options, %{})
Common.start_pleroma() Common.start_pleroma()
with {:ok, token} <- UserInviteToken.create_token(options) do with {:ok, invite} <- UserInviteToken.create_invite(options) do
Mix.shell().info( Mix.shell().info(
"Generated user invite token " <> String.replace(token.token_type, "_", " ") "Generated user invite token " <> String.replace(invite.invite_type, "_", " ")
) )
url = url =
Pleroma.Web.Router.Helpers.redirect_url( Pleroma.Web.Router.Helpers.redirect_url(
Pleroma.Web.Endpoint, Pleroma.Web.Endpoint,
:registration_page, :registration_page,
token.token invite.token
) )
IO.puts(url) IO.puts(url)
@ -367,7 +367,9 @@ def run(["invites_list"]) do
def run(["invite_revoke", token]) do def run(["invite_revoke", token]) do
Common.start_pleroma() Common.start_pleroma()
with {:ok, _} <- UserInviteToken.mark_as_used(token) do invite = UserInviteToken.find_by_token!(token)
with {:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
Mix.shell().info("Invite for token #{token} was revoked.") Mix.shell().info("Invite for token #{token} was revoked.")
else else
_ -> Mix.shell().error("No invite found with token #{token}") _ -> Mix.shell().error("No invite found with token #{token}")

View file

@ -17,106 +17,101 @@ defmodule Pleroma.UserInviteToken do
field(:used, :boolean, default: false) field(:used, :boolean, default: false)
field(:max_use, :integer) field(:max_use, :integer)
field(:expire_at, :date) field(:expire_at, :date)
field(:uses, :integer) field(:uses, :integer, default: 0)
field(:token_type) field(:invite_type, :string)
timestamps() timestamps()
end end
def create_token(options \\ []) do @spec create_invite(map()) :: UserInviteToken.t()
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() def create_invite(params \\ %{}) do
%UserInviteToken{}
max_use = options[:max_use] |> cast(params, ~w(max_use expire_at)a)
expire_at = options[:expire_at] |> add_token()
|> assign_type()
token = |> Repo.insert()
%UserInviteToken{
used: false,
token: token,
max_use: max_use,
expire_at: expire_at,
uses: 0
}
|> token_type()
Repo.insert(token)
end end
defp add_token(changeset) do
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
put_change(changeset, :token, token)
end
defp assign_type(%{changes: %{max_use: _max_use, expire_at: _expire_at}} = changeset) do
put_change(changeset, :invite_type, "reusable_date_limited")
end
defp assign_type(%{changes: %{expire_at: _expire_at}} = changeset) do
put_change(changeset, :invite_type, "date_limited")
end
defp assign_type(%{changes: %{max_use: _max_use}} = changeset) do
put_change(changeset, :invite_type, "reusable")
end
defp assign_type(changeset), do: put_change(changeset, :invite_type, "one_time")
@spec list_invites() :: [UserInviteToken.t()]
def list_invites do def list_invites do
query = from(u in UserInviteToken, order_by: u.id) query = from(u in UserInviteToken, order_by: u.id)
Repo.all(query) Repo.all(query)
end end
def used_changeset(struct) do @spec update_invite!(UserInviteToken.t(), map()) :: UserInviteToken.t() | no_return()
struct def update_invite!(invite, changes) do
|> cast(%{}, []) change(invite, changes) |> Repo.update!()
|> put_change(:used, true)
end end
@spec mark_as_used(token()) :: {:ok, UserInviteToken.t()} | {:error, token()} @spec update_invite(UserInviteToken.t(), map()) ::
def mark_as_used(token) do {:ok, UserInviteToken.t()} | {:error, Changeset.t()}
with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}), def update_invite(invite, changes) do
{:ok, token} <- Repo.update(used_changeset(token)) do change(invite, changes) |> Repo.update()
{:ok, token}
else
_e -> {:error, token}
end
end end
defp token_type(%{expire_at: nil, max_use: nil} = token), do: %{token | token_type: "one_time"} @spec find_by_token!(token()) :: UserInviteToken.t() | no_return()
def find_by_token!(token), do: Repo.get_by!(UserInviteToken, token: token)
defp token_type(%{expire_at: _expire_at, max_use: nil} = token), @spec valid_invite?(UserInviteToken.t()) :: boolean()
do: %{token | token_type: "date_limited"} def valid_invite?(%{invite_type: "one_time"} = invite) do
not invite.used
defp token_type(%{expire_at: nil, max_use: _max_use} = token),
do: %{token | token_type: "reusable"}
defp token_type(%{expire_at: _expire_at, max_use: _max_use} = token),
do: %{token | token_type: "reusable_date_limited"}
@spec valid_token?(UserInviteToken.t()) :: boolean()
def valid_token?(%{token_type: "one_time"} = token) do
not token.used
end end
def valid_token?(%{token_type: "date_limited"} = token) do def valid_invite?(%{invite_type: "date_limited"} = invite) do
not_overdue_date?(token) and not token.used not_overdue_date?(invite) and not invite.used
end end
def valid_token?(%{token_type: "reusable"} = token) do def valid_invite?(%{invite_type: "reusable"} = invite) do
token.uses < token.max_use and not token.used invite.uses < invite.max_use and not invite.used
end end
def valid_token?(%{token_type: "reusable_date_limited"} = token) do def valid_invite?(%{invite_type: "reusable_date_limited"} = invite) do
not_overdue_date?(token) and token.uses < token.max_use and not token.used not_overdue_date?(invite) and invite.uses < invite.max_use and not invite.used
end end
defp not_overdue_date?(%{expire_at: expire_at} = token) do defp not_overdue_date?(%{expire_at: expire_at} = invite) do
Date.compare(Date.utc_today(), expire_at) in [:lt, :eq] || Date.compare(Date.utc_today(), expire_at) in [:lt, :eq] ||
(Repo.update!(change(token, used: true)) && false) (update_invite!(invite, %{used: true}) && false)
end end
def update_usage(%{token_type: "date_limited"}), do: nil @spec update_usage!(UserInviteToken.t()) :: nil | UserInviteToken.t() | no_return()
def update_usage!(%{invite_type: "date_limited"}), do: nil
def update_usage(%{token_type: "one_time"} = token) do def update_usage!(%{invite_type: "one_time"} = invite),
UserInviteToken.mark_as_used(token.token) do: update_invite!(invite, %{used: true})
end
def update_usage(%{token_type: token_type} = token)
when token_type == "reusable" or token_type == "reusable_date_limited" do
new_uses = token.uses + 1
def update_usage!(%{invite_type: invite_type} = invite)
when invite_type == "reusable" or invite_type == "reusable_date_limited" do
changes = %{ changes = %{
uses: new_uses uses: invite.uses + 1
} }
changes = changes =
if new_uses >= token.max_use do if changes.uses >= invite.max_use do
Map.put(changes, :used, true) Map.put(changes, :used, true)
else else
changes changes
end end
change(token, changes) |> Repo.update!() update_invite!(invite, changes)
end end
end end

View file

@ -129,7 +129,7 @@ def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do
end end
def register_user(params) do def register_user(params) do
token_string = params["token"] token = params["token"]
params = %{ params = %{
nickname: params["nickname"], nickname: params["nickname"],
@ -163,29 +163,29 @@ def register_user(params) do
{:error, %{error: Jason.encode!(%{captcha: [error]})}} {:error, %{error: Jason.encode!(%{captcha: [error]})}}
else else
registrations_open = Pleroma.Config.get([:instance, :registrations_open]) registrations_open = Pleroma.Config.get([:instance, :registrations_open])
registration_process(registrations_open, params, token_string) registration_process(registrations_open, params, token)
end end
end end
defp registration_process(_registration_open = true, params, _token_string) do defp registration_process(_registration_open = true, params, _token) do
create_user(params) create_user(params)
end end
defp registration_process(registration_open, params, token_string) defp registration_process(registration_open, params, token)
when registration_open == false or is_nil(registration_open) do when registration_open == false or is_nil(registration_open) do
token = invite =
unless is_nil(token_string) do unless is_nil(token) do
Repo.get_by(UserInviteToken, %{token: token_string}) Repo.get_by(UserInviteToken, %{token: token})
end end
valid_token? = token && UserInviteToken.valid_token?(token) valid_invite? = invite && UserInviteToken.valid_invite?(invite)
case token do case invite do
nil -> nil ->
{:error, "Invalid token"} {:error, "Invalid token"}
token when valid_token? -> invite when valid_invite? ->
UserInviteToken.update_usage(token) UserInviteToken.update_usage!(invite)
create_user(params) create_user(params)
_ -> _ ->

View file

@ -6,7 +6,7 @@ def change do
add(:expire_at, :date) add(:expire_at, :date)
add(:uses, :integer, default: 0) add(:uses, :integer, default: 0)
add(:max_use, :integer) add(:max_use, :integer)
add(:token_type, :string, default: "one_time") add(:invite_type, :string, default: "one_time")
end end
end end
end end

View file

@ -292,10 +292,10 @@ test "token is generated with max use and expire date" do
describe "running invites_list" do describe "running invites_list" do
test "invites are listed" do test "invites are listed" do
{:ok, invite} = Pleroma.UserInviteToken.create_token() {:ok, invite} = Pleroma.UserInviteToken.create_invite()
{:ok, invite2} = {:ok, invite2} =
Pleroma.UserInviteToken.create_token(expire_at: Date.utc_today(), max_use: 15) Pleroma.UserInviteToken.create_invite(%{expire_at: Date.utc_today(), max_use: 15})
assert capture_io(fn -> assert capture_io(fn ->
Mix.Tasks.Pleroma.User.run([ Mix.Tasks.Pleroma.User.run([
@ -314,7 +314,7 @@ test "invites are listed" do
describe "running invite revoke" do describe "running invite revoke" do
test "invite is revoked" do test "invite is revoked" do
{:ok, invite} = Pleroma.UserInviteToken.create_token(expire_at: Date.utc_today()) {:ok, invite} = Pleroma.UserInviteToken.create_invite(%{expire_at: Date.utc_today()})
assert capture_io(fn -> assert capture_io(fn ->
Mix.Tasks.Pleroma.User.run([ Mix.Tasks.Pleroma.User.run([

View file

@ -370,7 +370,7 @@ test "it registers a new user and parses mentions in the bio" do
end end
test "returns user on success" do test "returns user on success" do
{:ok, token} = UserInviteToken.create_token() {:ok, invite} = UserInviteToken.create_invite()
data = %{ data = %{
"nickname" => "vinny", "nickname" => "vinny",
@ -379,15 +379,15 @@ test "returns user on success" do
"bio" => "streamer", "bio" => "streamer",
"password" => "hiptofbees", "password" => "hiptofbees",
"confirm" => "hiptofbees", "confirm" => "hiptofbees",
"token" => token.token "token" => invite.token
} }
{:ok, user} = TwitterAPI.register_user(data) {:ok, user} = TwitterAPI.register_user(data)
fetched_user = User.get_by_nickname("vinny") fetched_user = User.get_by_nickname("vinny")
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
assert token.used == true assert invite.used == true
assert UserView.render("show.json", %{user: user}) == assert UserView.render("show.json", %{user: user}) ==
UserView.render("show.json", %{user: fetched_user}) UserView.render("show.json", %{user: fetched_user})
@ -411,8 +411,8 @@ test "returns error on invalid token" do
end end
test "returns error on expired token" do test "returns error on expired token" do
{:ok, token} = UserInviteToken.create_token() {:ok, invite} = UserInviteToken.create_invite()
UserInviteToken.mark_as_used(token.token) UserInviteToken.update_invite!(invite, used: true)
data = %{ data = %{
"nickname" => "GrimReaper", "nickname" => "GrimReaper",
@ -421,7 +421,7 @@ test "returns error on expired token" do
"bio" => "Your time has come", "bio" => "Your time has come",
"password" => "scythe", "password" => "scythe",
"confirm" => "scythe", "confirm" => "scythe",
"token" => token.token "token" => invite.token
} }
{:error, msg} = TwitterAPI.register_user(data) {:error, msg} = TwitterAPI.register_user(data)
@ -449,8 +449,8 @@ test "returns error on expired token" do
"confirm" => "hiptofbees" "confirm" => "hiptofbees"
} }
check_fn = fn token -> check_fn = fn invite ->
data = Map.put(data, "token", token.token) data = Map.put(data, "token", invite.token)
{:ok, user} = TwitterAPI.register_user(data) {:ok, user} = TwitterAPI.register_user(data)
fetched_user = User.get_by_nickname("vinny") fetched_user = User.get_by_nickname("vinny")
@ -462,37 +462,37 @@ test "returns error on expired token" do
end end
test "returns user on success", %{check_fn: check_fn} do test "returns user on success", %{check_fn: check_fn} do
{:ok, token} = UserInviteToken.create_token(expire_at: Date.utc_today()) {:ok, invite} = UserInviteToken.create_invite(%{expire_at: Date.utc_today()})
check_fn.(token) check_fn.(invite)
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
refute token.used refute invite.used
end end
test "returns user on token which expired tomorrow", %{check_fn: check_fn} do test "returns user on token which expired tomorrow", %{check_fn: check_fn} do
{:ok, token} = UserInviteToken.create_token(expire_at: Date.add(Date.utc_today(), 1)) {:ok, invite} = UserInviteToken.create_invite(%{expire_at: Date.add(Date.utc_today(), 1)})
check_fn.(token) check_fn.(invite)
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
refute token.used refute invite.used
end end
test "returns an error on overdue date", %{data: data} do test "returns an error on overdue date", %{data: data} do
{:ok, token} = UserInviteToken.create_token(expire_at: Date.add(Date.utc_today(), -1)) {:ok, invite} = UserInviteToken.create_invite(%{expire_at: Date.add(Date.utc_today(), -1)})
data = Map.put(data, "token", token.token) data = Map.put(data, "token", invite.token)
{:error, msg} = TwitterAPI.register_user(data) {:error, msg} = TwitterAPI.register_user(data)
assert msg == "Expired token" assert msg == "Expired token"
refute User.get_by_nickname("vinny") refute User.get_by_nickname("vinny")
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
assert token.used == true assert invite.used == true
end end
end end
@ -509,9 +509,9 @@ test "returns an error on overdue date", %{data: data} do
end end
test "returns user on success, after him registration fails" do test "returns user on success, after him registration fails" do
{:ok, token} = UserInviteToken.create_token(max_use: 100) {:ok, invite} = UserInviteToken.create_invite(%{max_use: 100})
Ecto.Changeset.change(token, uses: 99) |> Repo.update!() UserInviteToken.update_invite!(invite, uses: 99)
data = %{ data = %{
"nickname" => "vinny", "nickname" => "vinny",
@ -520,14 +520,14 @@ test "returns user on success, after him registration fails" do
"bio" => "streamer", "bio" => "streamer",
"password" => "hiptofbees", "password" => "hiptofbees",
"confirm" => "hiptofbees", "confirm" => "hiptofbees",
"token" => token.token "token" => invite.token
} }
{:ok, user} = TwitterAPI.register_user(data) {:ok, user} = TwitterAPI.register_user(data)
fetched_user = User.get_by_nickname("vinny") fetched_user = User.get_by_nickname("vinny")
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
assert token.used == true assert invite.used == true
assert UserView.render("show.json", %{user: user}) == assert UserView.render("show.json", %{user: user}) ==
UserView.render("show.json", %{user: fetched_user}) UserView.render("show.json", %{user: fetched_user})
@ -539,7 +539,7 @@ test "returns user on success, after him registration fails" do
"bio" => "Your time has come", "bio" => "Your time has come",
"password" => "scythe", "password" => "scythe",
"confirm" => "scythe", "confirm" => "scythe",
"token" => token.token "token" => invite.token
} }
{:error, msg} = TwitterAPI.register_user(data) {:error, msg} = TwitterAPI.register_user(data)
@ -562,11 +562,7 @@ test "returns user on success, after him registration fails" do
end end
test "returns user on success" do test "returns user on success" do
{:ok, token} = {:ok, invite} = UserInviteToken.create_invite(%{expire_at: Date.utc_today(), max_use: 100})
UserInviteToken.create_token(
expire_at: Date.utc_today(),
max_use: 100
)
data = %{ data = %{
"nickname" => "vinny", "nickname" => "vinny",
@ -575,27 +571,23 @@ test "returns user on success" do
"bio" => "streamer", "bio" => "streamer",
"password" => "hiptofbees", "password" => "hiptofbees",
"confirm" => "hiptofbees", "confirm" => "hiptofbees",
"token" => token.token "token" => invite.token
} }
{:ok, user} = TwitterAPI.register_user(data) {:ok, user} = TwitterAPI.register_user(data)
fetched_user = User.get_by_nickname("vinny") fetched_user = User.get_by_nickname("vinny")
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
refute token.used refute invite.used
assert UserView.render("show.json", %{user: user}) == assert UserView.render("show.json", %{user: user}) ==
UserView.render("show.json", %{user: fetched_user}) UserView.render("show.json", %{user: fetched_user})
end end
test "error after max uses" do test "error after max uses" do
{:ok, token} = {:ok, invite} = UserInviteToken.create_invite(%{expire_at: Date.utc_today(), max_use: 100})
UserInviteToken.create_token(
expire_at: Date.utc_today(),
max_use: 100
)
Ecto.Changeset.change(token, uses: 99) |> Repo.update!() UserInviteToken.update_invite!(invite, uses: 99)
data = %{ data = %{
"nickname" => "vinny", "nickname" => "vinny",
@ -604,13 +596,13 @@ test "error after max uses" do
"bio" => "streamer", "bio" => "streamer",
"password" => "hiptofbees", "password" => "hiptofbees",
"confirm" => "hiptofbees", "confirm" => "hiptofbees",
"token" => token.token "token" => invite.token
} }
{:ok, user} = TwitterAPI.register_user(data) {:ok, user} = TwitterAPI.register_user(data)
fetched_user = User.get_by_nickname("vinny") fetched_user = User.get_by_nickname("vinny")
token = Repo.get_by(UserInviteToken, token: token.token) invite = Repo.get_by(UserInviteToken, token: invite.token)
assert token.used == true assert invite.used == true
assert UserView.render("show.json", %{user: user}) == assert UserView.render("show.json", %{user: user}) ==
UserView.render("show.json", %{user: fetched_user}) UserView.render("show.json", %{user: fetched_user})
@ -622,7 +614,7 @@ test "error after max uses" do
"bio" => "Your time has come", "bio" => "Your time has come",
"password" => "scythe", "password" => "scythe",
"confirm" => "scythe", "confirm" => "scythe",
"token" => token.token "token" => invite.token
} }
{:error, msg} = TwitterAPI.register_user(data) {:error, msg} = TwitterAPI.register_user(data)
@ -632,11 +624,8 @@ test "error after max uses" do
end end
test "returns error on overdue date" do test "returns error on overdue date" do
{:ok, token} = {:ok, invite} =
UserInviteToken.create_token( UserInviteToken.create_invite(%{expire_at: Date.add(Date.utc_today(), -1), max_use: 100})
expire_at: Date.add(Date.utc_today(), -1),
max_use: 100
)
data = %{ data = %{
"nickname" => "GrimReaper", "nickname" => "GrimReaper",
@ -645,7 +634,7 @@ test "returns error on overdue date" do
"bio" => "Your time has come", "bio" => "Your time has come",
"password" => "scythe", "password" => "scythe",
"confirm" => "scythe", "confirm" => "scythe",
"token" => token.token "token" => invite.token
} }
{:error, msg} = TwitterAPI.register_user(data) {:error, msg} = TwitterAPI.register_user(data)
@ -655,13 +644,10 @@ test "returns error on overdue date" do
end end
test "returns error on with overdue date and after max" do test "returns error on with overdue date and after max" do
{:ok, token} = {:ok, invite} =
UserInviteToken.create_token( UserInviteToken.create_invite(%{expire_at: Date.add(Date.utc_today(), -1), max_use: 100})
expire_at: Date.add(Date.utc_today(), -1),
max_use: 100
)
Ecto.Changeset.change(token, uses: 100) |> Repo.update!() UserInviteToken.update_invite!(invite, uses: 100)
data = %{ data = %{
"nickname" => "GrimReaper", "nickname" => "GrimReaper",
@ -670,7 +656,7 @@ test "returns error on with overdue date and after max" do
"bio" => "Your time has come", "bio" => "Your time has come",
"password" => "scythe", "password" => "scythe",
"confirm" => "scythe", "confirm" => "scythe",
"token" => token.token "token" => invite.token
} }
{:error, msg} = TwitterAPI.register_user(data) {:error, msg} = TwitterAPI.register_user(data)