First
[anni] / lib / pleroma / web / twitter_api / twitter_api.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
6   import Pleroma.Web.Gettext
7
8   alias Pleroma.Emails.Mailer
9   alias Pleroma.Emails.UserEmail
10   alias Pleroma.Repo
11   alias Pleroma.User
12   alias Pleroma.UserInviteToken
13
14   def register_user(params, opts \\ []) do
15     fallback_language = Gettext.get_locale()
16
17     params =
18       params
19       |> Map.take([:email, :token, :password])
20       |> Map.put(:bio, params |> Map.get(:bio, "") |> User.parse_bio())
21       |> Map.put(:nickname, params[:username])
22       |> Map.put(:name, Map.get(params, :fullname, params[:username]))
23       |> Map.put(:password_confirmation, params[:password])
24       |> Map.put(:registration_reason, params[:reason])
25       |> Map.put(:birthday, params[:birthday])
26       |> Map.put(
27         :language,
28         Pleroma.Web.Gettext.normalize_locale(params[:language]) || fallback_language
29       )
30
31     if Pleroma.Config.get([:instance, :registrations_open]) do
32       create_user(params, opts)
33     else
34       create_user_with_invite(params, opts)
35     end
36   end
37
38   defp create_user_with_invite(params, opts) do
39     with %{token: token} when is_binary(token) <- params,
40          %UserInviteToken{} = invite <- Repo.get_by(UserInviteToken, %{token: token}),
41          true <- UserInviteToken.valid_invite?(invite) do
42       UserInviteToken.update_usage!(invite)
43       create_user(params, opts)
44     else
45       nil -> {:error, "Invalid token"}
46       _ -> {:error, "Expired token"}
47     end
48   end
49
50   defp create_user(params, opts) do
51     changeset = User.register_changeset(%User{}, params, opts)
52
53     case User.register(changeset) do
54       {:ok, user} ->
55         {:ok, user}
56
57       {:error, changeset} ->
58         errors =
59           changeset
60           |> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end)
61           |> Jason.encode!()
62
63         {:error, errors}
64     end
65   end
66
67   def password_reset(nickname_or_email) do
68     with true <- is_binary(nickname_or_email),
69          %User{local: true, email: email, is_active: true} = user when is_binary(email) <-
70            User.get_by_nickname_or_email(nickname_or_email),
71          {:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do
72       user
73       |> UserEmail.password_reset_email(token_record.token)
74       |> Mailer.deliver_async()
75
76       {:ok, :enqueued}
77     else
78       _ ->
79         {:ok, :noop}
80     end
81   end
82
83   def validate_captcha(app, params) do
84     if app.trusted || not Pleroma.Captcha.enabled?() do
85       :ok
86     else
87       do_validate_captcha(params)
88     end
89   end
90
91   defp do_validate_captcha(params) do
92     with :ok <- validate_captcha_presence(params),
93          :ok <-
94            Pleroma.Captcha.validate(
95              params[:captcha_token],
96              params[:captcha_solution],
97              params[:captcha_answer_data]
98            ) do
99       :ok
100     else
101       {:error, :captcha_error} ->
102         captcha_error(dgettext("errors", "CAPTCHA Error"))
103
104       {:error, :invalid} ->
105         captcha_error(dgettext("errors", "Invalid CAPTCHA"))
106
107       {:error, :kocaptcha_service_unavailable} ->
108         captcha_error(dgettext("errors", "Kocaptcha service unavailable"))
109
110       {:error, :expired} ->
111         captcha_error(dgettext("errors", "CAPTCHA expired"))
112
113       {:error, :already_used} ->
114         captcha_error(dgettext("errors", "CAPTCHA already used"))
115
116       {:error, :invalid_answer_data} ->
117         captcha_error(dgettext("errors", "Invalid answer data"))
118
119       {:error, error} ->
120         captcha_error(error)
121     end
122   end
123
124   defp validate_captcha_presence(params) do
125     [:captcha_solution, :captcha_token, :captcha_answer_data]
126     |> Enum.find_value(:ok, fn key ->
127       unless is_binary(params[key]) do
128         error = dgettext("errors", "Invalid CAPTCHA (Missing parameter: %{name})", name: key)
129         {:error, error}
130       end
131     end)
132   end
133
134   # For some reason FE expects error message to be a serialized JSON
135   defp captcha_error(error), do: {:error, Jason.encode!(%{captcha: [error]})}
136 end