[#923] OAuth consumer controller tests. Misc. improvements.
[akkoma] / lib / pleroma / web / twitter_api / controllers / util_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.TwitterAPI.UtilController do
6 use Pleroma.Web, :controller
7
8 require Logger
9
10 alias Comeonin.Pbkdf2
11 alias Pleroma.Emoji
12 alias Pleroma.Notification
13 alias Pleroma.PasswordResetToken
14 alias Pleroma.Repo
15 alias Pleroma.User
16 alias Pleroma.Web
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.OStatus
20 alias Pleroma.Web.WebFinger
21
22 def show_password_reset(conn, %{"token" => token}) do
23 with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
24 %User{} = user <- User.get_by_id(token.user_id) do
25 render(conn, "password_reset.html", %{
26 token: token,
27 user: user
28 })
29 else
30 _e -> render(conn, "invalid_token.html")
31 end
32 end
33
34 def password_reset(conn, %{"data" => data}) do
35 with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
36 render(conn, "password_reset_success.html")
37 else
38 _e -> render(conn, "password_reset_failed.html")
39 end
40 end
41
42 def help_test(conn, _params) do
43 json(conn, "ok")
44 end
45
46 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
47 with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do
48 conn
49 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
50 else
51 _e ->
52 render(conn, "subscribe.html", %{
53 nickname: nick,
54 avatar: nil,
55 error: "Could not find user"
56 })
57 end
58 end
59
60 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
61 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
62 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
63 conn
64 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
65 else
66 _e ->
67 render(conn, "subscribe.html", %{
68 nickname: nick,
69 avatar: nil,
70 error: "Something went wrong."
71 })
72 end
73 end
74
75 def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
76 {err, followee} = OStatus.find_or_make_user(acct)
77 avatar = User.avatar_url(followee)
78 name = followee.nickname
79 id = followee.id
80
81 if !!user do
82 conn
83 |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
84 else
85 conn
86 |> render("follow_login.html", %{
87 error: false,
88 acct: acct,
89 avatar: avatar,
90 name: name,
91 id: id
92 })
93 end
94 end
95
96 def do_remote_follow(conn, %{
97 "authorization" => %{"name" => username, "password" => password, "id" => id}
98 }) do
99 followee = User.get_by_id(id)
100 avatar = User.avatar_url(followee)
101 name = followee.nickname
102
103 with %User{} = user <- User.get_cached_by_nickname(username),
104 true <- Pbkdf2.checkpw(password, user.password_hash),
105 %User{} = _followed <- User.get_by_id(id),
106 {:ok, follower} <- User.follow(user, followee),
107 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
108 conn
109 |> render("followed.html", %{error: false})
110 else
111 # Was already following user
112 {:error, "Could not follow user:" <> _rest} ->
113 render(conn, "followed.html", %{error: false})
114
115 _e ->
116 conn
117 |> render("follow_login.html", %{
118 error: "Wrong username or password",
119 id: id,
120 name: name,
121 avatar: avatar
122 })
123 end
124 end
125
126 def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
127 with %User{} = followee <- User.get_by_id(id),
128 {:ok, follower} <- User.follow(user, followee),
129 {:ok, _activity} <- ActivityPub.follow(follower, followee) do
130 conn
131 |> render("followed.html", %{error: false})
132 else
133 # Was already following user
134 {:error, "Could not follow user:" <> _rest} ->
135 conn
136 |> render("followed.html", %{error: false})
137
138 e ->
139 Logger.debug("Remote follow failed with error #{inspect(e)}")
140
141 conn
142 |> render("followed.html", %{error: inspect(e)})
143 end
144 end
145
146 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
147 with {:ok, _} <- Notification.read_one(user, notification_id) do
148 json(conn, %{status: "success"})
149 else
150 {:error, message} ->
151 conn
152 |> put_resp_content_type("application/json")
153 |> send_resp(403, Jason.encode!(%{"error" => message}))
154 end
155 end
156
157 def config(conn, _params) do
158 instance = Pleroma.Config.get(:instance)
159 instance_fe = Pleroma.Config.get(:fe)
160 instance_chat = Pleroma.Config.get(:chat)
161
162 case get_format(conn) do
163 "xml" ->
164 response = """
165 <config>
166 <site>
167 <name>#{Keyword.get(instance, :name)}</name>
168 <site>#{Web.base_url()}</site>
169 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
170 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
171 </site>
172 </config>
173 """
174
175 conn
176 |> put_resp_content_type("application/xml")
177 |> send_resp(200, response)
178
179 _ ->
180 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
181
182 uploadlimit = %{
183 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
184 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
185 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
186 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
187 }
188
189 data = %{
190 name: Keyword.get(instance, :name),
191 description: Keyword.get(instance, :description),
192 server: Web.base_url(),
193 textlimit: to_string(Keyword.get(instance, :limit)),
194 uploadlimit: uploadlimit,
195 closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
196 private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
197 vapidPublicKey: vapid_public_key,
198 accountActivationRequired:
199 if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"),
200 invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"),
201 safeDMMentionsEnabled:
202 if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0")
203 }
204
205 pleroma_fe =
206 if instance_fe do
207 %{
208 theme: Keyword.get(instance_fe, :theme),
209 background: Keyword.get(instance_fe, :background),
210 logo: Keyword.get(instance_fe, :logo),
211 logoMask: Keyword.get(instance_fe, :logo_mask),
212 logoMargin: Keyword.get(instance_fe, :logo_margin),
213 redirectRootNoLogin: Keyword.get(instance_fe, :redirect_root_no_login),
214 redirectRootLogin: Keyword.get(instance_fe, :redirect_root_login),
215 chatDisabled: !Keyword.get(instance_chat, :enabled),
216 showInstanceSpecificPanel: Keyword.get(instance_fe, :show_instance_panel),
217 scopeOptionsEnabled: Keyword.get(instance_fe, :scope_options_enabled),
218 formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),
219 collapseMessageWithSubject:
220 Keyword.get(instance_fe, :collapse_message_with_subject),
221 hidePostStats: Keyword.get(instance_fe, :hide_post_stats),
222 hideUserStats: Keyword.get(instance_fe, :hide_user_stats),
223 scopeCopy: Keyword.get(instance_fe, :scope_copy),
224 subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior),
225 alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)
226 }
227 else
228 Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
229 end
230
231 managed_config = Keyword.get(instance, :managed_config)
232
233 data =
234 if managed_config do
235 data |> Map.put("pleromafe", pleroma_fe)
236 else
237 data
238 end
239
240 json(conn, %{site: data})
241 end
242 end
243
244 def frontend_configurations(conn, _params) do
245 config =
246 Pleroma.Config.get(:frontend_configurations, %{})
247 |> Enum.into(%{})
248
249 json(conn, config)
250 end
251
252 def version(conn, _params) do
253 version = Pleroma.Application.named_version()
254
255 case get_format(conn) do
256 "xml" ->
257 response = "<version>#{version}</version>"
258
259 conn
260 |> put_resp_content_type("application/xml")
261 |> send_resp(200, response)
262
263 _ ->
264 json(conn, version)
265 end
266 end
267
268 def emoji(conn, _params) do
269 json(conn, Enum.into(Emoji.get_all(), %{}))
270 end
271
272 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
273 follow_import(conn, %{"list" => File.read!(listfile.path)})
274 end
275
276 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
277 with followed_identifiers <- String.split(list),
278 {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do
279 json(conn, "job started")
280 end
281 end
282
283 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
284 blocks_import(conn, %{"list" => File.read!(listfile.path)})
285 end
286
287 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
288 with blocked_identifiers <- String.split(list),
289 {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do
290 json(conn, "job started")
291 end
292 end
293
294 def change_password(%{assigns: %{user: user}} = conn, params) do
295 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
296 {:ok, user} ->
297 with {:ok, _user} <-
298 User.reset_password(user, %{
299 password: params["new_password"],
300 password_confirmation: params["new_password_confirmation"]
301 }) do
302 json(conn, %{status: "success"})
303 else
304 {:error, changeset} ->
305 {_, {error, _}} = Enum.at(changeset.errors, 0)
306 json(conn, %{error: "New password #{error}."})
307
308 _ ->
309 json(conn, %{error: "Unable to change password."})
310 end
311
312 {:error, msg} ->
313 json(conn, %{error: msg})
314 end
315 end
316
317 def delete_account(%{assigns: %{user: user}} = conn, params) do
318 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
319 {:ok, user} ->
320 Task.start(fn -> User.delete(user) end)
321 json(conn, %{status: "success"})
322
323 {:error, msg} ->
324 json(conn, %{error: msg})
325 end
326 end
327
328 def captcha(conn, _params) do
329 json(conn, Pleroma.Captcha.new())
330 end
331 end