Merge remote-tracking branch 'upstream/develop' into by-approval
[akkoma] / lib / pleroma / web / twitter_api / twitter_api.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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.HTML
11 alias Pleroma.Repo
12 alias Pleroma.User
13 alias Pleroma.UserInviteToken
14
15 def register_user(params, opts \\ []) do
16 params =
17 params
18 |> Map.take([:email, :token, :password])
19 |> Map.put(:bio, params |> Map.get(:bio, "") |> User.parse_bio())
20 |> Map.put(:nickname, params[:username])
21 |> Map.put(:name, Map.get(params, :fullname, params[:username]))
22 |> Map.put(:password_confirmation, params[:password])
23 |> Map.put(:registration_reason, HTML.strip_tags(params[:reason]))
24
25 if Pleroma.Config.get([:instance, :registrations_open]) do
26 create_user(params, opts)
27 else
28 create_user_with_invite(params, opts)
29 end
30 end
31
32 defp create_user_with_invite(params, opts) do
33 with %{token: token} when is_binary(token) <- params,
34 %UserInviteToken{} = invite <- Repo.get_by(UserInviteToken, %{token: token}),
35 true <- UserInviteToken.valid_invite?(invite) do
36 UserInviteToken.update_usage!(invite)
37 create_user(params, opts)
38 else
39 nil -> {:error, "Invalid token"}
40 _ -> {:error, "Expired token"}
41 end
42 end
43
44 defp create_user(params, opts) do
45 changeset = User.register_changeset(%User{}, params, opts)
46
47 case User.register(changeset) do
48 {:ok, user} ->
49 maybe_notify_admins(user)
50 {:ok, user}
51
52 {:error, changeset} ->
53 errors =
54 changeset
55 |> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end)
56 |> Jason.encode!()
57
58 {:error, errors}
59 end
60 end
61
62 defp maybe_notify_admins(%User{} = account) do
63 if Pleroma.Config.get([:instance, :account_approval_required]) do
64 User.all_superusers()
65 |> Enum.filter(fn user -> not is_nil(user.email) end)
66 |> Enum.each(fn superuser ->
67 superuser
68 |> Pleroma.Emails.AdminEmail.new_unapproved_registration(account)
69 |> Pleroma.Emails.Mailer.deliver_async()
70 end)
71 end
72 end
73
74 def password_reset(nickname_or_email) do
75 with true <- is_binary(nickname_or_email),
76 %User{local: true, email: email} = user when is_binary(email) <-
77 User.get_by_nickname_or_email(nickname_or_email),
78 {:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do
79 user
80 |> UserEmail.password_reset_email(token_record.token)
81 |> Mailer.deliver_async()
82
83 {:ok, :enqueued}
84 else
85 false ->
86 {:error, "bad user identifier"}
87
88 %User{local: true, email: nil} ->
89 {:ok, :noop}
90
91 %User{local: false} ->
92 {:error, "remote user"}
93
94 nil ->
95 {:error, "unknown user"}
96 end
97 end
98
99 def validate_captcha(app, params) do
100 if app.trusted || not Pleroma.Captcha.enabled?() do
101 :ok
102 else
103 do_validate_captcha(params)
104 end
105 end
106
107 defp do_validate_captcha(params) do
108 with :ok <- validate_captcha_presence(params),
109 :ok <-
110 Pleroma.Captcha.validate(
111 params[:captcha_token],
112 params[:captcha_solution],
113 params[:captcha_answer_data]
114 ) do
115 :ok
116 else
117 {:error, :captcha_error} ->
118 captcha_error(dgettext("errors", "CAPTCHA Error"))
119
120 {:error, :invalid} ->
121 captcha_error(dgettext("errors", "Invalid CAPTCHA"))
122
123 {:error, :kocaptcha_service_unavailable} ->
124 captcha_error(dgettext("errors", "Kocaptcha service unavailable"))
125
126 {:error, :expired} ->
127 captcha_error(dgettext("errors", "CAPTCHA expired"))
128
129 {:error, :already_used} ->
130 captcha_error(dgettext("errors", "CAPTCHA already used"))
131
132 {:error, :invalid_answer_data} ->
133 captcha_error(dgettext("errors", "Invalid answer data"))
134
135 {:error, error} ->
136 captcha_error(error)
137 end
138 end
139
140 defp validate_captcha_presence(params) do
141 [:captcha_solution, :captcha_token, :captcha_answer_data]
142 |> Enum.find_value(:ok, fn key ->
143 unless is_binary(params[key]) do
144 error = dgettext("errors", "Invalid CAPTCHA (Missing parameter: %{name})", name: key)
145 {:error, error}
146 end
147 end)
148 end
149
150 # For some reason FE expects error message to be a serialized JSON
151 defp captcha_error(error), do: {:error, Jason.encode!(%{captcha: [error]})}
152 end