mirror of
https://akkoma.dev/AkkomaGang/akkoma.git
synced 2024-11-09 17:55:11 +00:00
Merge branch 'feature/767-multiple-use-invite-token' into 'develop'
Feature/767 multiple use invite token See merge request pleroma/pleroma!1032
This commit is contained in:
commit
e5d553aa45
|
@ -200,11 +200,64 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
## `/api/pleroma/admin/invite_token`
|
## `/api/pleroma/admin/invite_token`
|
||||||
|
|
||||||
### Get a account registeration invite token
|
### Get an account registration invite token
|
||||||
|
|
||||||
|
- Methods: `GET`
|
||||||
|
- Params:
|
||||||
|
- *optional* `invite` => [
|
||||||
|
- *optional* `max_use` (integer)
|
||||||
|
- *optional* `expires_at` (date string e.g. "2019-04-07")
|
||||||
|
]
|
||||||
|
- Response: invite token (base64 string)
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/invites`
|
||||||
|
|
||||||
|
### Get a list of generated invites
|
||||||
|
|
||||||
- Methods: `GET`
|
- Methods: `GET`
|
||||||
- Params: none
|
- Params: none
|
||||||
- Response: invite token (base64 string)
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
|
||||||
|
"invites": [
|
||||||
|
{
|
||||||
|
"id": integer,
|
||||||
|
"token": string,
|
||||||
|
"used": boolean,
|
||||||
|
"expires_at": date,
|
||||||
|
"uses": integer,
|
||||||
|
"max_use": integer,
|
||||||
|
"invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/revoke_invite`
|
||||||
|
|
||||||
|
### Revoke invite by token
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `token`
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"id": integer,
|
||||||
|
"token": string,
|
||||||
|
"used": boolean,
|
||||||
|
"expires_at": date,
|
||||||
|
"uses": integer,
|
||||||
|
"max_use": integer,
|
||||||
|
"invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## `/api/pleroma/admin/email_invite`
|
## `/api/pleroma/admin/email_invite`
|
||||||
|
|
||||||
|
@ -213,7 +266,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- Methods: `POST`
|
- Methods: `POST`
|
||||||
- Params:
|
- Params:
|
||||||
- `email`
|
- `email`
|
||||||
- `name`, optionnal
|
- `name`, optional
|
||||||
|
|
||||||
## `/api/pleroma/admin/password_reset`
|
## `/api/pleroma/admin/password_reset`
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Mix.Tasks.Pleroma.Common
|
alias Mix.Tasks.Pleroma.Common
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
@shortdoc "Manages Pleroma users"
|
@shortdoc "Manages Pleroma users"
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
|
@ -26,7 +27,19 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||||
|
|
||||||
## Generate an invite link.
|
## Generate an invite link.
|
||||||
|
|
||||||
mix pleroma.user invite
|
mix pleroma.user invite [OPTION...]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `--expires_at DATE` - last day on which token is active (e.g. "2019-04-05")
|
||||||
|
- `--max_use NUMBER` - maximum numbers of token uses
|
||||||
|
|
||||||
|
## List generated invites
|
||||||
|
|
||||||
|
mix pleroma.user invites
|
||||||
|
|
||||||
|
## Revoke invite
|
||||||
|
|
||||||
|
mix pleroma.user revoke_invite TOKEN OR TOKEN_ID
|
||||||
|
|
||||||
## Delete the user's account.
|
## Delete the user's account.
|
||||||
|
|
||||||
|
@ -287,23 +300,79 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(["invite"]) do
|
def run(["invite" | rest]) do
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(rest,
|
||||||
|
strict: [
|
||||||
|
expires_at: :string,
|
||||||
|
max_use: :integer
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
options =
|
||||||
|
options
|
||||||
|
|> Keyword.update(:expires_at, {:ok, nil}, fn
|
||||||
|
nil -> {:ok, nil}
|
||||||
|
val -> Date.from_iso8601(val)
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
Common.start_pleroma()
|
Common.start_pleroma()
|
||||||
|
|
||||||
with {:ok, token} <- Pleroma.UserInviteToken.create_token() do
|
with {:ok, val} <- options[:expires_at],
|
||||||
Mix.shell().info("Generated user invite token")
|
options = Map.put(options, :expires_at, val),
|
||||||
|
{:ok, invite} <- UserInviteToken.create_invite(options) do
|
||||||
|
Mix.shell().info(
|
||||||
|
"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)
|
||||||
else
|
else
|
||||||
_ ->
|
error ->
|
||||||
Mix.shell().error("Could not create invite token.")
|
Mix.shell().error("Could not create invite token: #{inspect(error)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["invites"]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
Mix.shell().info("Invites list:")
|
||||||
|
|
||||||
|
UserInviteToken.list_invites()
|
||||||
|
|> Enum.each(fn invite ->
|
||||||
|
expire_info =
|
||||||
|
with expires_at when not is_nil(expires_at) <- invite.expires_at do
|
||||||
|
" | Expires at: #{Date.to_string(expires_at)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
using_info =
|
||||||
|
with max_use when not is_nil(max_use) <- invite.max_use do
|
||||||
|
" | Max use: #{max_use} Left use: #{max_use - invite.uses}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Mix.shell().info(
|
||||||
|
"ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{
|
||||||
|
invite.used
|
||||||
|
}#{expire_info}#{using_info}"
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["revoke_invite", token]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||||
|
{:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
|
||||||
|
Mix.shell().info("Invite for token #{token} was revoked.")
|
||||||
|
else
|
||||||
|
_ -> Mix.shell().error("No invite found with token #{token}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,40 +6,119 @@ defmodule Pleroma.UserInviteToken do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
@type token :: String.t()
|
||||||
|
|
||||||
schema "user_invite_tokens" do
|
schema "user_invite_tokens" do
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
field(:used, :boolean, default: false)
|
field(:used, :boolean, default: false)
|
||||||
|
field(:max_use, :integer)
|
||||||
|
field(:expires_at, :date)
|
||||||
|
field(:uses, :integer, default: 0)
|
||||||
|
field(:invite_type, :string)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_token do
|
@spec create_invite(map()) :: UserInviteToken.t()
|
||||||
|
def create_invite(params \\ %{}) do
|
||||||
|
%UserInviteToken{}
|
||||||
|
|> cast(params, [:max_use, :expires_at])
|
||||||
|
|> add_token()
|
||||||
|
|> assign_type()
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_token(changeset) do
|
||||||
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
|
token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
|
||||||
|
put_change(changeset, :token, token)
|
||||||
token = %UserInviteToken{
|
|
||||||
used: false,
|
|
||||||
token: token
|
|
||||||
}
|
|
||||||
|
|
||||||
Repo.insert(token)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def used_changeset(struct) do
|
defp assign_type(%{changes: %{max_use: _max_use, expires_at: _expires_at}} = changeset) do
|
||||||
struct
|
put_change(changeset, :invite_type, "reusable_date_limited")
|
||||||
|> cast(%{}, [])
|
|
||||||
|> put_change(:used, true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_as_used(token) do
|
defp assign_type(%{changes: %{expires_at: _expires_at}} = changeset) do
|
||||||
with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}),
|
put_change(changeset, :invite_type, "date_limited")
|
||||||
{:ok, token} <- Repo.update(used_changeset(token)) do
|
end
|
||||||
{:ok, token}
|
|
||||||
else
|
defp assign_type(%{changes: %{max_use: _max_use}} = changeset) do
|
||||||
_e -> {:error, token}
|
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
|
||||||
|
query = from(u in UserInviteToken, order_by: u.id)
|
||||||
|
Repo.all(query)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_invite!(UserInviteToken.t(), map()) :: UserInviteToken.t() | no_return()
|
||||||
|
def update_invite!(invite, changes) do
|
||||||
|
change(invite, changes) |> Repo.update!()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_invite(UserInviteToken.t(), map()) ::
|
||||||
|
{:ok, UserInviteToken.t()} | {:error, Changeset.t()}
|
||||||
|
def update_invite(invite, changes) do
|
||||||
|
change(invite, changes) |> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec find_by_token!(token()) :: UserInviteToken.t() | no_return()
|
||||||
|
def find_by_token!(token), do: Repo.get_by!(UserInviteToken, token: token)
|
||||||
|
|
||||||
|
@spec find_by_token(token()) :: {:ok, UserInviteToken.t()} | nil
|
||||||
|
def find_by_token(token) do
|
||||||
|
with invite <- Repo.get_by(UserInviteToken, token: token) do
|
||||||
|
{:ok, invite}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec valid_invite?(UserInviteToken.t()) :: boolean()
|
||||||
|
def valid_invite?(%{invite_type: "one_time"} = invite) do
|
||||||
|
not invite.used
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_invite?(%{invite_type: "date_limited"} = invite) do
|
||||||
|
not_overdue_date?(invite) and not invite.used
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_invite?(%{invite_type: "reusable"} = invite) do
|
||||||
|
invite.uses < invite.max_use and not invite.used
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_invite?(%{invite_type: "reusable_date_limited"} = invite) do
|
||||||
|
not_overdue_date?(invite) and invite.uses < invite.max_use and not invite.used
|
||||||
|
end
|
||||||
|
|
||||||
|
defp not_overdue_date?(%{expires_at: expires_at}) do
|
||||||
|
Date.compare(Date.utc_today(), expires_at) in [:lt, :eq]
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_usage!(UserInviteToken.t()) :: nil | UserInviteToken.t() | no_return()
|
||||||
|
def update_usage!(%{invite_type: "date_limited"}), do: nil
|
||||||
|
|
||||||
|
def update_usage!(%{invite_type: "one_time"} = invite),
|
||||||
|
do: update_invite!(invite, %{used: true})
|
||||||
|
|
||||||
|
def update_usage!(%{invite_type: invite_type} = invite)
|
||||||
|
when invite_type == "reusable" or invite_type == "reusable_date_limited" do
|
||||||
|
changes = %{
|
||||||
|
uses: invite.uses + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
changes =
|
||||||
|
if changes.uses >= invite.max_use do
|
||||||
|
Map.put(changes, :used, true)
|
||||||
|
else
|
||||||
|
changes
|
||||||
|
end
|
||||||
|
|
||||||
|
update_invite!(invite, changes)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
|
@ -235,7 +236,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
with true <-
|
with true <-
|
||||||
Pleroma.Config.get([:instance, :invites_enabled]) &&
|
Pleroma.Config.get([:instance, :invites_enabled]) &&
|
||||||
!Pleroma.Config.get([:instance, :registrations_open]),
|
!Pleroma.Config.get([:instance, :registrations_open]),
|
||||||
{:ok, invite_token} <- Pleroma.UserInviteToken.create_token(),
|
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||||
email <-
|
email <-
|
||||||
Pleroma.UserEmail.user_invitation_email(user, invite_token, email, params["name"]),
|
Pleroma.UserEmail.user_invitation_email(user, invite_token, email, params["name"]),
|
||||||
{:ok, _} <- Pleroma.Mailer.deliver(email) do
|
{:ok, _} <- Pleroma.Mailer.deliver(email) do
|
||||||
|
@ -244,11 +245,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Get a account registeration invite token (base64 string)"
|
@doc "Get a account registeration invite token (base64 string)"
|
||||||
def get_invite_token(conn, _params) do
|
def get_invite_token(conn, params) do
|
||||||
{:ok, token} = Pleroma.UserInviteToken.create_token()
|
options = params["invite"] || %{}
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(options)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(token.token)
|
|> json(invite.token)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Get list of created invites"
|
||||||
|
def invites(conn, _params) do
|
||||||
|
invites = UserInviteToken.list_invites()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(AccountView.render("invites.json", %{invites: invites}))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Revokes invite by token"
|
||||||
|
def revoke_invite(conn, %{"token" => token}) do
|
||||||
|
invite = UserInviteToken.find_by_token!(token)
|
||||||
|
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(AccountView.render("invite.json", %{invite: updated_invite}))
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Get a password reset token (base64 string) for given nickname"
|
@doc "Get a password reset token (base64 string) for given nickname"
|
||||||
|
|
|
@ -26,4 +26,22 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||||
"tags" => user.tags || []
|
"tags" => user.tags || []
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("invite.json", %{invite: invite}) do
|
||||||
|
%{
|
||||||
|
"id" => invite.id,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => invite.used,
|
||||||
|
"expires_at" => invite.expires_at,
|
||||||
|
"uses" => invite.uses,
|
||||||
|
"max_use" => invite.max_use,
|
||||||
|
"invite_type" => invite.invite_type
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("invites.json", %{invites: invites}) do
|
||||||
|
%{
|
||||||
|
invites: render_many(invites, AccountView, "invite.json", as: :invite)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -168,6 +168,8 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/relay", AdminAPIController, :relay_unfollow)
|
delete("/relay", AdminAPIController, :relay_unfollow)
|
||||||
|
|
||||||
get("/invite_token", AdminAPIController, :get_invite_token)
|
get("/invite_token", AdminAPIController, :get_invite_token)
|
||||||
|
get("/invites", AdminAPIController, :invites)
|
||||||
|
post("/revoke_invite", AdminAPIController, :revoke_invite)
|
||||||
post("/email_invite", AdminAPIController, :email_invite)
|
post("/email_invite", AdminAPIController, :email_invite)
|
||||||
|
|
||||||
get("/password_reset", AdminAPIController, :get_password_reset)
|
get("/password_reset", AdminAPIController, :get_password_reset)
|
||||||
|
|
|
@ -129,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI 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,36 +163,49 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI 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)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# no need to query DB if registration is open
|
defp registration_process(registration_open, params, token)
|
||||||
token =
|
when registration_open == false or is_nil(registration_open) do
|
||||||
unless registrations_open || is_nil(token_string) do
|
invite =
|
||||||
Repo.get_by(UserInviteToken, %{token: token_string})
|
unless is_nil(token) do
|
||||||
end
|
Repo.get_by(UserInviteToken, %{token: token})
|
||||||
|
|
||||||
cond do
|
|
||||||
registrations_open || (!is_nil(token) && !token.used) ->
|
|
||||||
changeset = User.register_changeset(%User{}, params)
|
|
||||||
|
|
||||||
with {:ok, user} <- User.register(changeset) do
|
|
||||||
!registrations_open && UserInviteToken.mark_as_used(token.token)
|
|
||||||
|
|
||||||
{:ok, user}
|
|
||||||
else
|
|
||||||
{:error, changeset} ->
|
|
||||||
errors =
|
|
||||||
Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|
|
||||||
|> Jason.encode!()
|
|
||||||
|
|
||||||
{:error, %{error: errors}}
|
|
||||||
end
|
|
||||||
|
|
||||||
!registrations_open && is_nil(token) ->
|
|
||||||
{:error, "Invalid token"}
|
|
||||||
|
|
||||||
!registrations_open && token.used ->
|
|
||||||
{:error, "Expired token"}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
valid_invite? = invite && UserInviteToken.valid_invite?(invite)
|
||||||
|
|
||||||
|
case invite do
|
||||||
|
nil ->
|
||||||
|
{:error, "Invalid token"}
|
||||||
|
|
||||||
|
invite when valid_invite? ->
|
||||||
|
UserInviteToken.update_usage!(invite)
|
||||||
|
create_user(params)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "Expired token"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp registration_process(true, params, _token) do
|
||||||
|
create_user(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_user(params) do
|
||||||
|
changeset = User.register_changeset(%User{}, params)
|
||||||
|
|
||||||
|
case User.register(changeset) do
|
||||||
|
{:ok, user} ->
|
||||||
|
{:ok, user}
|
||||||
|
|
||||||
|
{:error, changeset} ->
|
||||||
|
errors =
|
||||||
|
Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|
||||||
|
|> Jason.encode!()
|
||||||
|
|
||||||
|
{:error, %{error: errors}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddFieldsToUserInviteTokens do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:user_invite_tokens) do
|
||||||
|
add(:expires_at, :date)
|
||||||
|
add(:uses, :integer, default: 0)
|
||||||
|
add(:max_use, :integer)
|
||||||
|
add(:invite_type, :string, default: "one_time")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
64
test/fixtures/lambadalambda.json
vendored
Normal file
64
test/fixtures/lambadalambda.json
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"featured": {
|
||||||
|
"@id": "toot:featured",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"movedTo": {
|
||||||
|
"@id": "as:movedTo",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"schema": "http://schema.org#",
|
||||||
|
"PropertyValue": "schema:PropertyValue",
|
||||||
|
"value": "schema:value",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"Emoji": "toot:Emoji",
|
||||||
|
"IdentityProof": "toot:IdentityProof",
|
||||||
|
"focalPoint": {
|
||||||
|
"@container": "@list",
|
||||||
|
"@id": "toot:focalPoint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://mastodon.social/users/lambadalambda",
|
||||||
|
"type": "Person",
|
||||||
|
"following": "https://mastodon.social/users/lambadalambda/following",
|
||||||
|
"followers": "https://mastodon.social/users/lambadalambda/followers",
|
||||||
|
"inbox": "https://mastodon.social/users/lambadalambda/inbox",
|
||||||
|
"outbox": "https://mastodon.social/users/lambadalambda/outbox",
|
||||||
|
"featured": "https://mastodon.social/users/lambadalambda/collections/featured",
|
||||||
|
"preferredUsername": "lambadalambda",
|
||||||
|
"name": "Critical Value",
|
||||||
|
"summary": "\u003cp\u003e\u003c/p\u003e",
|
||||||
|
"url": "https://mastodon.social/@lambadalambda",
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"publicKey": {
|
||||||
|
"id": "https://mastodon.social/users/lambadalambda#main-key",
|
||||||
|
"owner": "https://mastodon.social/users/lambadalambda",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"tag": [],
|
||||||
|
"attachment": [],
|
||||||
|
"endpoints": {
|
||||||
|
"sharedInbox": "https://mastodon.social/inbox"
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"type": "Image",
|
||||||
|
"mediaType": "image/gif",
|
||||||
|
"url": "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "Image",
|
||||||
|
"mediaType": "image/gif",
|
||||||
|
"url": "https://files.mastodon.social/accounts/headers/000/000/264/original/28b26104f83747d2.gif"
|
||||||
|
}
|
||||||
|
}
|
|
@ -716,6 +716,10 @@ defmodule HttpRequestMock do
|
||||||
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.atom")}}
|
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.atom")}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("https://mastodon.social/users/lambadalambda", _, _, _) do
|
||||||
|
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
|
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
|
||||||
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
|
||||||
end
|
end
|
||||||
|
|
|
@ -245,7 +245,87 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
||||||
end) =~ "http"
|
end) =~ "http"
|
||||||
|
|
||||||
assert_received {:mix_shell, :info, [message]}
|
assert_received {:mix_shell, :info, [message]}
|
||||||
assert message =~ "Generated"
|
assert message =~ "Generated user invite token one time"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "token is generated with expires_at" do
|
||||||
|
assert capture_io(fn ->
|
||||||
|
Mix.Tasks.Pleroma.User.run([
|
||||||
|
"invite",
|
||||||
|
"--expires-at",
|
||||||
|
Date.to_string(Date.utc_today())
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message =~ "Generated user invite token date limited"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "token is generated with max use" do
|
||||||
|
assert capture_io(fn ->
|
||||||
|
Mix.Tasks.Pleroma.User.run([
|
||||||
|
"invite",
|
||||||
|
"--max-use",
|
||||||
|
"5"
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message =~ "Generated user invite token reusable"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "token is generated with max use and expires date" do
|
||||||
|
assert capture_io(fn ->
|
||||||
|
Mix.Tasks.Pleroma.User.run([
|
||||||
|
"invite",
|
||||||
|
"--max-use",
|
||||||
|
"5",
|
||||||
|
"--expires-at",
|
||||||
|
Date.to_string(Date.utc_today())
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message =~ "Generated user invite token reusable date limited"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "running invites" do
|
||||||
|
test "invites are listed" do
|
||||||
|
{:ok, invite} = Pleroma.UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
{:ok, invite2} =
|
||||||
|
Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 15})
|
||||||
|
|
||||||
|
# assert capture_io(fn ->
|
||||||
|
Mix.Tasks.Pleroma.User.run([
|
||||||
|
"invites"
|
||||||
|
])
|
||||||
|
|
||||||
|
# end)
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert_received {:mix_shell, :info, [message2]}
|
||||||
|
assert_received {:mix_shell, :info, [message3]}
|
||||||
|
assert message =~ "Invites list:"
|
||||||
|
assert message2 =~ invite.invite_type
|
||||||
|
assert message3 =~ invite2.invite_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "running revoke_invite" do
|
||||||
|
test "invite is revoked" do
|
||||||
|
{:ok, invite} = Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today()})
|
||||||
|
|
||||||
|
assert capture_io(fn ->
|
||||||
|
Mix.Tasks.Pleroma.User.run([
|
||||||
|
"revoke_invite",
|
||||||
|
invite.token
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert_received {:mix_shell, :info, [message]}
|
||||||
|
assert message =~ "Invite for token #{invite.token} was revoked."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
96
test/user_invite_token_test.exs
Normal file
96
test/user_invite_token_test.exs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
defmodule Pleroma.UserInviteTokenTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
describe "valid_invite?/1 one time invites" do
|
||||||
|
setup do
|
||||||
|
invite = %UserInviteToken{invite_type: "one_time"}
|
||||||
|
|
||||||
|
{:ok, invite: invite}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not used returns true", %{invite: invite} do
|
||||||
|
invite = %{invite | used: false}
|
||||||
|
assert UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "used returns false", %{invite: invite} do
|
||||||
|
invite = %{invite | used: true}
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "valid_invite?/1 reusable invites" do
|
||||||
|
setup do
|
||||||
|
invite = %UserInviteToken{
|
||||||
|
invite_type: "reusable",
|
||||||
|
max_use: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, invite: invite}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with less uses then max use returns true", %{invite: invite} do
|
||||||
|
invite = %{invite | uses: 4}
|
||||||
|
assert UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with equal or more uses then max use returns false", %{invite: invite} do
|
||||||
|
invite = %{invite | uses: 5}
|
||||||
|
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
|
||||||
|
invite = %{invite | uses: 6}
|
||||||
|
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "valid_token?/1 date limited invites" do
|
||||||
|
setup do
|
||||||
|
invite = %UserInviteToken{invite_type: "date_limited"}
|
||||||
|
{:ok, invite: invite}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "expires today returns true", %{invite: invite} do
|
||||||
|
invite = %{invite | expires_at: Date.utc_today()}
|
||||||
|
assert UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "expires yesterday returns false", %{invite: invite} do
|
||||||
|
invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)}
|
||||||
|
invite = Repo.insert!(invite)
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "valid_token?/1 reusable date limited invites" do
|
||||||
|
setup do
|
||||||
|
invite = %UserInviteToken{invite_type: "reusable_date_limited", max_use: 5}
|
||||||
|
{:ok, invite: invite}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not overdue date and less uses returns true", %{invite: invite} do
|
||||||
|
invite = %{invite | expires_at: Date.utc_today(), uses: 4}
|
||||||
|
assert UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "overdue date and less uses returns false", %{invite: invite} do
|
||||||
|
invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)}
|
||||||
|
invite = Repo.insert!(invite)
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "not overdue date with more uses returns false", %{invite: invite} do
|
||||||
|
invite = %{invite | expires_at: Date.utc_today(), uses: 5}
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "overdue date with more uses returns false", %{invite: invite} do
|
||||||
|
invite = %{invite | expires_at: Date.add(Date.utc_today(), -1), uses: 5}
|
||||||
|
invite = Repo.insert!(invite)
|
||||||
|
refute UserInviteToken.valid_invite?(invite)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "/api/pleroma/admin/user" do
|
describe "/api/pleroma/admin/user" do
|
||||||
|
@ -640,4 +641,136 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
"tags" => []
|
"tags" => []
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/invite_token" do
|
||||||
|
test "without options" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/invite_token")
|
||||||
|
|
||||||
|
token = json_response(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(token)
|
||||||
|
refute invite.used
|
||||||
|
refute invite.expires_at
|
||||||
|
refute invite.max_use
|
||||||
|
assert invite.invite_type == "one_time"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with expires_at" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/invite_token", %{
|
||||||
|
"invite" => %{"expires_at" => Date.to_string(Date.utc_today())}
|
||||||
|
})
|
||||||
|
|
||||||
|
token = json_response(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(token)
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
assert invite.expires_at == Date.utc_today()
|
||||||
|
refute invite.max_use
|
||||||
|
assert invite.invite_type == "date_limited"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with max_use" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/invite_token", %{
|
||||||
|
"invite" => %{"max_use" => 150}
|
||||||
|
})
|
||||||
|
|
||||||
|
token = json_response(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(token)
|
||||||
|
refute invite.used
|
||||||
|
refute invite.expires_at
|
||||||
|
assert invite.max_use == 150
|
||||||
|
assert invite.invite_type == "reusable"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with max use and expires_at" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/invite_token", %{
|
||||||
|
"invite" => %{"max_use" => 150, "expires_at" => Date.to_string(Date.utc_today())}
|
||||||
|
})
|
||||||
|
|
||||||
|
token = json_response(conn, 200)
|
||||||
|
invite = UserInviteToken.find_by_token!(token)
|
||||||
|
refute invite.used
|
||||||
|
assert invite.expires_at == Date.utc_today()
|
||||||
|
assert invite.max_use == 150
|
||||||
|
assert invite.invite_type == "reusable_date_limited"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/invites" do
|
||||||
|
test "no invites" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/invites")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{"invites" => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with invite" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/invites")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"invites" => [
|
||||||
|
%{
|
||||||
|
"expires_at" => nil,
|
||||||
|
"id" => invite.id,
|
||||||
|
"invite_type" => "one_time",
|
||||||
|
"max_use" => nil,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => false,
|
||||||
|
"uses" => 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/revoke_invite" do
|
||||||
|
test "with token" do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> post("/api/pleroma/admin/revoke_invite", %{"token" => invite.token})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"expires_at" => nil,
|
||||||
|
"id" => invite.id,
|
||||||
|
"invite_type" => "one_time",
|
||||||
|
"max_use" => nil,
|
||||||
|
"token" => invite.token,
|
||||||
|
"used" => true,
|
||||||
|
"uses" => 0
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
test "create a status" do
|
test "create a status" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
|
mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
|
||||||
|
@ -299,7 +304,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||||
UserView.render("show.json", %{user: fetched_user})
|
UserView.render("show.json", %{user: fetched_user})
|
||||||
end
|
end
|
||||||
|
|
||||||
@moduletag skip: "needs 'account_activation_required: true' in config"
|
|
||||||
test "it sends confirmation email if :account_activation_required is specified in instance config" do
|
test "it sends confirmation email if :account_activation_required is specified in instance config" do
|
||||||
setting = Pleroma.Config.get([:instance, :account_activation_required])
|
setting = Pleroma.Config.get([:instance, :account_activation_required])
|
||||||
|
|
||||||
|
@ -353,68 +357,313 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||||
assert user2.bio == expected_text
|
assert user2.bio == expected_text
|
||||||
end
|
end
|
||||||
|
|
||||||
@moduletag skip: "needs 'registrations_open: false' in config"
|
describe "register with one time token" do
|
||||||
test "it registers a new user via invite token and returns the user." do
|
setup do
|
||||||
{:ok, token} = UserInviteToken.create_token()
|
setting = Pleroma.Config.get([:instance, :registrations_open])
|
||||||
|
|
||||||
data = %{
|
if setting do
|
||||||
"nickname" => "vinny",
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
"email" => "pasta@pizza.vs",
|
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
||||||
"fullname" => "Vinny Vinesauce",
|
end
|
||||||
"bio" => "streamer",
|
|
||||||
"password" => "hiptofbees",
|
|
||||||
"confirm" => "hiptofbees",
|
|
||||||
"token" => token.token
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, user} = TwitterAPI.register_user(data)
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
fetched_user = User.get_by_nickname("vinny")
|
test "returns user on success" do
|
||||||
token = Repo.get_by(UserInviteToken, token: token.token)
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
|
||||||
assert token.used == true
|
data = %{
|
||||||
|
"nickname" => "vinny",
|
||||||
|
"email" => "pasta@pizza.vs",
|
||||||
|
"fullname" => "Vinny Vinesauce",
|
||||||
|
"bio" => "streamer",
|
||||||
|
"password" => "hiptofbees",
|
||||||
|
"confirm" => "hiptofbees",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
assert UserView.render("show.json", %{user: user}) ==
|
{:ok, user} = TwitterAPI.register_user(data)
|
||||||
UserView.render("show.json", %{user: fetched_user})
|
|
||||||
|
fetched_user = User.get_by_nickname("vinny")
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
|
assert invite.used == true
|
||||||
|
|
||||||
|
assert UserView.render("show.json", %{user: user}) ==
|
||||||
|
UserView.render("show.json", %{user: fetched_user})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error on invalid token" do
|
||||||
|
data = %{
|
||||||
|
"nickname" => "GrimReaper",
|
||||||
|
"email" => "death@reapers.afterlife",
|
||||||
|
"fullname" => "Reaper Grim",
|
||||||
|
"bio" => "Your time has come",
|
||||||
|
"password" => "scythe",
|
||||||
|
"confirm" => "scythe",
|
||||||
|
"token" => "DudeLetMeInImAFairy"
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Invalid token"
|
||||||
|
refute User.get_by_nickname("GrimReaper")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error on expired token" do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite()
|
||||||
|
UserInviteToken.update_invite!(invite, used: true)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "GrimReaper",
|
||||||
|
"email" => "death@reapers.afterlife",
|
||||||
|
"fullname" => "Reaper Grim",
|
||||||
|
"bio" => "Your time has come",
|
||||||
|
"password" => "scythe",
|
||||||
|
"confirm" => "scythe",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Expired token"
|
||||||
|
refute User.get_by_nickname("GrimReaper")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@moduletag skip: "needs 'registrations_open: false' in config"
|
describe "registers with date limited token" do
|
||||||
test "it returns an error if invalid token submitted" do
|
setup do
|
||||||
data = %{
|
setting = Pleroma.Config.get([:instance, :registrations_open])
|
||||||
"nickname" => "GrimReaper",
|
|
||||||
"email" => "death@reapers.afterlife",
|
|
||||||
"fullname" => "Reaper Grim",
|
|
||||||
"bio" => "Your time has come",
|
|
||||||
"password" => "scythe",
|
|
||||||
"confirm" => "scythe",
|
|
||||||
"token" => "DudeLetMeInImAFairy"
|
|
||||||
}
|
|
||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
if setting do
|
||||||
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
|
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
||||||
|
end
|
||||||
|
|
||||||
assert msg == "Invalid token"
|
data = %{
|
||||||
refute User.get_by_nickname("GrimReaper")
|
"nickname" => "vinny",
|
||||||
|
"email" => "pasta@pizza.vs",
|
||||||
|
"fullname" => "Vinny Vinesauce",
|
||||||
|
"bio" => "streamer",
|
||||||
|
"password" => "hiptofbees",
|
||||||
|
"confirm" => "hiptofbees"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_fn = fn invite ->
|
||||||
|
data = Map.put(data, "token", invite.token)
|
||||||
|
{:ok, user} = TwitterAPI.register_user(data)
|
||||||
|
fetched_user = User.get_by_nickname("vinny")
|
||||||
|
|
||||||
|
assert UserView.render("show.json", %{user: user}) ==
|
||||||
|
UserView.render("show.json", %{user: fetched_user})
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, data: data, check_fn: check_fn}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns user on success", %{check_fn: check_fn} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today()})
|
||||||
|
|
||||||
|
check_fn.(invite)
|
||||||
|
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns user on token which expired tomorrow", %{check_fn: check_fn} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), 1)})
|
||||||
|
|
||||||
|
check_fn.(invite)
|
||||||
|
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error on overdue date", %{data: data} do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1)})
|
||||||
|
|
||||||
|
data = Map.put(data, "token", invite.token)
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Expired token"
|
||||||
|
refute User.get_by_nickname("vinny")
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@moduletag skip: "needs 'registrations_open: false' in config"
|
describe "registers with reusable token" do
|
||||||
test "it returns an error if expired token submitted" do
|
setup do
|
||||||
{:ok, token} = UserInviteToken.create_token()
|
setting = Pleroma.Config.get([:instance, :registrations_open])
|
||||||
UserInviteToken.mark_as_used(token.token)
|
|
||||||
|
|
||||||
data = %{
|
if setting do
|
||||||
"nickname" => "GrimReaper",
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
"email" => "death@reapers.afterlife",
|
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
||||||
"fullname" => "Reaper Grim",
|
end
|
||||||
"bio" => "Your time has come",
|
|
||||||
"password" => "scythe",
|
|
||||||
"confirm" => "scythe",
|
|
||||||
"token" => token.token
|
|
||||||
}
|
|
||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
assert msg == "Expired token"
|
test "returns user on success, after him registration fails" do
|
||||||
refute User.get_by_nickname("GrimReaper")
|
{:ok, invite} = UserInviteToken.create_invite(%{max_use: 100})
|
||||||
|
|
||||||
|
UserInviteToken.update_invite!(invite, uses: 99)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "vinny",
|
||||||
|
"email" => "pasta@pizza.vs",
|
||||||
|
"fullname" => "Vinny Vinesauce",
|
||||||
|
"bio" => "streamer",
|
||||||
|
"password" => "hiptofbees",
|
||||||
|
"confirm" => "hiptofbees",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, user} = TwitterAPI.register_user(data)
|
||||||
|
fetched_user = User.get_by_nickname("vinny")
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
|
assert invite.used == true
|
||||||
|
|
||||||
|
assert UserView.render("show.json", %{user: user}) ==
|
||||||
|
UserView.render("show.json", %{user: fetched_user})
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "GrimReaper",
|
||||||
|
"email" => "death@reapers.afterlife",
|
||||||
|
"fullname" => "Reaper Grim",
|
||||||
|
"bio" => "Your time has come",
|
||||||
|
"password" => "scythe",
|
||||||
|
"confirm" => "scythe",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Expired token"
|
||||||
|
refute User.get_by_nickname("GrimReaper")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "registers with reusable date limited token" do
|
||||||
|
setup do
|
||||||
|
setting = Pleroma.Config.get([:instance, :registrations_open])
|
||||||
|
|
||||||
|
if setting do
|
||||||
|
Pleroma.Config.put([:instance, :registrations_open], false)
|
||||||
|
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns user on success" do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "vinny",
|
||||||
|
"email" => "pasta@pizza.vs",
|
||||||
|
"fullname" => "Vinny Vinesauce",
|
||||||
|
"bio" => "streamer",
|
||||||
|
"password" => "hiptofbees",
|
||||||
|
"confirm" => "hiptofbees",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, user} = TwitterAPI.register_user(data)
|
||||||
|
fetched_user = User.get_by_nickname("vinny")
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
|
refute invite.used
|
||||||
|
|
||||||
|
assert UserView.render("show.json", %{user: user}) ==
|
||||||
|
UserView.render("show.json", %{user: fetched_user})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "error after max uses" do
|
||||||
|
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
|
||||||
|
|
||||||
|
UserInviteToken.update_invite!(invite, uses: 99)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "vinny",
|
||||||
|
"email" => "pasta@pizza.vs",
|
||||||
|
"fullname" => "Vinny Vinesauce",
|
||||||
|
"bio" => "streamer",
|
||||||
|
"password" => "hiptofbees",
|
||||||
|
"confirm" => "hiptofbees",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, user} = TwitterAPI.register_user(data)
|
||||||
|
fetched_user = User.get_by_nickname("vinny")
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
assert invite.used == true
|
||||||
|
|
||||||
|
assert UserView.render("show.json", %{user: user}) ==
|
||||||
|
UserView.render("show.json", %{user: fetched_user})
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "GrimReaper",
|
||||||
|
"email" => "death@reapers.afterlife",
|
||||||
|
"fullname" => "Reaper Grim",
|
||||||
|
"bio" => "Your time has come",
|
||||||
|
"password" => "scythe",
|
||||||
|
"confirm" => "scythe",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Expired token"
|
||||||
|
refute User.get_by_nickname("GrimReaper")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error on overdue date" do
|
||||||
|
{:ok, invite} =
|
||||||
|
UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "GrimReaper",
|
||||||
|
"email" => "death@reapers.afterlife",
|
||||||
|
"fullname" => "Reaper Grim",
|
||||||
|
"bio" => "Your time has come",
|
||||||
|
"password" => "scythe",
|
||||||
|
"confirm" => "scythe",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Expired token"
|
||||||
|
refute User.get_by_nickname("GrimReaper")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error on with overdue date and after max" do
|
||||||
|
{:ok, invite} =
|
||||||
|
UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
|
||||||
|
|
||||||
|
UserInviteToken.update_invite!(invite, uses: 100)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"nickname" => "GrimReaper",
|
||||||
|
"email" => "death@reapers.afterlife",
|
||||||
|
"fullname" => "Reaper Grim",
|
||||||
|
"bio" => "Your time has come",
|
||||||
|
"password" => "scythe",
|
||||||
|
"confirm" => "scythe",
|
||||||
|
"token" => invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
|
assert msg == "Expired token"
|
||||||
|
refute User.get_by_nickname("GrimReaper")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns the error on registration problems" do
|
test "it returns the error on registration problems" do
|
||||||
|
|
Loading…
Reference in a new issue