--- /dev/null
+defmodule Pleroma.UserInviteToken do
+ use Ecto.Schema
+
+ import Ecto.Changeset
+
+ alias Pleroma.{User, UserInviteToken, Repo}
+
+ schema "user_invite_tokens" do
+ field(:token, :string)
+ field(:used, :boolean, default: false)
+
+ timestamps()
+ end
+
+ def create_token do
+ token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
+
+ token = %UserInviteToken{
+ used: false,
+ token: token
+ }
+
+ Repo.insert(token)
+ end
+
+ def used_changeset(struct) do
+ struct
+ |> cast(%{}, [])
+ |> put_change(:used, true)
+ end
+
+ def mark_as_used(token) do
+ with %{used: false} = token <- Repo.get_by(UserInviteToken, %{token: token}),
+ {:ok, token} <- Repo.update(used_changeset(token)) do
+ {:ok, token}
+ else
+ _e -> {:error, token}
+ end
+ end
+end
get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status)
get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation)
- if @registrations_open do
- post("/account/register", TwitterAPI.Controller, :register)
- end
+ post("/account/register", TwitterAPI.Controller, :register)
get("/search", TwitterAPI.Controller, :search)
get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline)
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
- alias Pleroma.{User, Activity, Repo, Object}
+ alias Pleroma.{UserInviteToken, User, Activity, Repo, Object}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.{OStatus, CommonAPI}
import Ecto.Query
+ @instance Application.get_env(:pleroma, :instance)
@httpoison Application.get_env(:pleroma, :httpoison)
+ @registrations_open Keyword.get(@instance, :registrations_open)
def create_status(%User{} = user, %{"status" => _} = data) do
CommonAPI.post(user, data)
end
def register_user(params) do
+ tokenString = params["token"]
+
params = %{
nickname: params["nickname"],
name: params["fullname"],
password_confirmation: params["confirm"]
}
- changeset = User.register_changeset(%User{}, params)
+ # no need to query DB if registration is open
+ unless @registrations_open || is_nil(tokenString) do
+ token = Repo.get_by(UserInviteToken, %{token: tokenString})
+ end
+
+ cond do
+ @registrations_open || !is_nil(token) && !token.used ->
+ changeset = User.register_changeset(%User{}, params)
- with {:ok, user} <- Repo.insert(changeset) do
- {:ok, user}
- else
- {:error, changeset} ->
- errors =
- Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
- |> Jason.encode!()
+ with {:ok, user} <- Repo.insert(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
- {:error, %{error: errors}}
+ !@registrations_open && is_nil(token) -> {:error, "Invalid token"}
+ !@registrations_open && token.used -> {:error, "Expired token"}
end
end
--- /dev/null
+defmodule Pleroma.Repo.Migrations.CreateUserInviteTokens do
+ use Ecto.Migration
+
+ def change do
+ create table(:user_invite_tokens) do
+ add :token, :string
+ add :used, :boolean, default: false
+
+ timestamps()
+ end
+ end
+end
use Pleroma.DataCase
alias Pleroma.Builders.UserBuilder
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView}
- alias Pleroma.{Activity, User, Object, Repo}
+ alias Pleroma.{Activity, User, Object, Repo, UserInviteToken}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.ActivityView
UserView.render("show.json", %{user: fetched_user})
end
+ @moduletag skip: "needs 'registrations_open: false' in config"
+ test "it registers a new user via invite token and returns the user." do
+ {:ok, token} = UserInviteToken.create_token()
+
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => token.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ fetched_user = Repo.get_by(User, nickname: "vinny")
+ token = Repo.get_by(UserInviteToken, token: token.token)
+
+ assert token.used == true
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ end
+
+ @moduletag skip: "needs 'registrations_open: false' in config"
+ test "it returns an error if invalid token submitted" 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 Repo.get_by(User, nickname: "GrimReaper")
+ end
+
+ @moduletag skip: "needs 'registrations_open: false' in config"
+ test "it returns an error if expired token submitted" do
+ {:ok, token} = UserInviteToken.create_token()
+ UserInviteToken.mark_as_used(token.token)
+
+ data = %{
+ "nickname" => "GrimReaper",
+ "email" => "death@reapers.afterlife",
+ "fullname" => "Reaper Grim",
+ "bio" => "Your time has come",
+ "password" => "scythe",
+ "confirm" => "scythe",
+ "token" => token.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute Repo.get_by(User, nickname: "GrimReaper")
+ end
+
test "it returns the error on registration problems" do
data = %{
"nickname" => "lain",