AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private...
[akkoma] / lib / pleroma / web / admin_api / admin_api_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.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
7 alias Pleroma.Activity
8 alias Pleroma.User
9 alias Pleroma.UserInviteToken
10 alias Pleroma.Web.ActivityPub.ActivityPub
11 alias Pleroma.Web.ActivityPub.Relay
12 alias Pleroma.Web.AdminAPI.AccountView
13 alias Pleroma.Web.AdminAPI.Config
14 alias Pleroma.Web.AdminAPI.ConfigView
15 alias Pleroma.Web.AdminAPI.ReportView
16 alias Pleroma.Web.AdminAPI.Search
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.StatusView
19
20 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
21
22 require Logger
23
24 @users_page_size 50
25
26 action_fallback(:errors)
27
28 def user_delete(conn, %{"nickname" => nickname}) do
29 User.get_cached_by_nickname(nickname)
30 |> User.delete()
31
32 conn
33 |> json(nickname)
34 end
35
36 def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
37 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
38 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
39 User.follow(follower, followed)
40 end
41
42 conn
43 |> json("ok")
44 end
45
46 def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
47 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
48 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
49 User.unfollow(follower, followed)
50 end
51
52 conn
53 |> json("ok")
54 end
55
56 def user_create(
57 conn,
58 %{"nickname" => nickname, "email" => email, "password" => password}
59 ) do
60 user_data = %{
61 nickname: nickname,
62 name: nickname,
63 email: email,
64 password: password,
65 password_confirmation: password,
66 bio: "."
67 }
68
69 changeset = User.register_changeset(%User{}, user_data, need_confirmation: false)
70 {:ok, user} = User.register(changeset)
71
72 conn
73 |> json(user.nickname)
74 end
75
76 def user_show(conn, %{"nickname" => nickname}) do
77 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
78 conn
79 |> json(AccountView.render("show.json", %{user: user}))
80 else
81 _ -> {:error, :not_found}
82 end
83 end
84
85 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
86 godmode = params["godmode"] == "true" || params["godmode"] == true
87
88 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
89 {_, page_size} = page_params(params)
90
91 activities =
92 ActivityPub.fetch_user_activities(user, nil, %{
93 "limit" => page_size,
94 "godmode" => godmode
95 })
96
97 conn
98 |> json(StatusView.render("index.json", %{activities: activities, as: :activity}))
99 else
100 _ -> {:error, :not_found}
101 end
102 end
103
104 def user_toggle_activation(conn, %{"nickname" => nickname}) do
105 user = User.get_cached_by_nickname(nickname)
106
107 {:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
108
109 conn
110 |> json(AccountView.render("show.json", %{user: updated_user}))
111 end
112
113 def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
114 with {:ok, _} <- User.tag(nicknames, tags),
115 do: json_response(conn, :no_content, "")
116 end
117
118 def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
119 with {:ok, _} <- User.untag(nicknames, tags),
120 do: json_response(conn, :no_content, "")
121 end
122
123 def list_users(conn, params) do
124 {page, page_size} = page_params(params)
125 filters = maybe_parse_filters(params["filters"])
126
127 search_params = %{
128 query: params["query"],
129 page: page,
130 page_size: page_size,
131 tags: params["tags"],
132 name: params["name"],
133 email: params["email"]
134 }
135
136 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
137 do:
138 conn
139 |> json(
140 AccountView.render("index.json",
141 users: users,
142 count: count,
143 page_size: page_size
144 )
145 )
146 end
147
148 @filters ~w(local external active deactivated is_admin is_moderator)
149
150 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
151 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
152
153 defp maybe_parse_filters(filters) do
154 filters
155 |> String.split(",")
156 |> Enum.filter(&Enum.member?(@filters, &1))
157 |> Enum.map(&String.to_atom(&1))
158 |> Enum.into(%{}, &{&1, true})
159 end
160
161 def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
162 when permission_group in ["moderator", "admin"] do
163 user = User.get_cached_by_nickname(nickname)
164
165 info =
166 %{}
167 |> Map.put("is_" <> permission_group, true)
168
169 info_cng = User.Info.admin_api_update(user.info, info)
170
171 cng =
172 user
173 |> Ecto.Changeset.change()
174 |> Ecto.Changeset.put_embed(:info, info_cng)
175
176 {:ok, _user} = User.update_and_set_cache(cng)
177
178 json(conn, info)
179 end
180
181 def right_add(conn, _) do
182 render_error(conn, :not_found, "No such permission_group")
183 end
184
185 def right_get(conn, %{"nickname" => nickname}) do
186 user = User.get_cached_by_nickname(nickname)
187
188 conn
189 |> json(%{
190 is_moderator: user.info.is_moderator,
191 is_admin: user.info.is_admin
192 })
193 end
194
195 def right_delete(
196 %{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
197 %{
198 "permission_group" => permission_group,
199 "nickname" => nickname
200 }
201 )
202 when permission_group in ["moderator", "admin"] do
203 if admin_nickname == nickname do
204 render_error(conn, :forbidden, "You can't revoke your own admin status.")
205 else
206 user = User.get_cached_by_nickname(nickname)
207
208 info =
209 %{}
210 |> Map.put("is_" <> permission_group, false)
211
212 info_cng = User.Info.admin_api_update(user.info, info)
213
214 cng =
215 Ecto.Changeset.change(user)
216 |> Ecto.Changeset.put_embed(:info, info_cng)
217
218 {:ok, _user} = User.update_and_set_cache(cng)
219
220 json(conn, info)
221 end
222 end
223
224 def right_delete(conn, _) do
225 render_error(conn, :not_found, "No such permission_group")
226 end
227
228 def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
229 with {:ok, status} <- Ecto.Type.cast(:boolean, status),
230 %User{} = user <- User.get_cached_by_nickname(nickname),
231 {:ok, _} <- User.deactivate(user, !status),
232 do: json_response(conn, :no_content, "")
233 end
234
235 def relay_follow(conn, %{"relay_url" => target}) do
236 with {:ok, _message} <- Relay.follow(target) do
237 json(conn, target)
238 else
239 _ ->
240 conn
241 |> put_status(500)
242 |> json(target)
243 end
244 end
245
246 def relay_unfollow(conn, %{"relay_url" => target}) do
247 with {:ok, _message} <- Relay.unfollow(target) do
248 json(conn, target)
249 else
250 _ ->
251 conn
252 |> put_status(500)
253 |> json(target)
254 end
255 end
256
257 @doc "Sends registration invite via email"
258 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
259 with true <-
260 Pleroma.Config.get([:instance, :invites_enabled]) &&
261 !Pleroma.Config.get([:instance, :registrations_open]),
262 {:ok, invite_token} <- UserInviteToken.create_invite(),
263 email <-
264 Pleroma.Emails.UserEmail.user_invitation_email(
265 user,
266 invite_token,
267 email,
268 params["name"]
269 ),
270 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
271 json_response(conn, :no_content, "")
272 end
273 end
274
275 @doc "Get a account registeration invite token (base64 string)"
276 def get_invite_token(conn, params) do
277 options = params["invite"] || %{}
278 {:ok, invite} = UserInviteToken.create_invite(options)
279
280 conn
281 |> json(invite.token)
282 end
283
284 @doc "Get list of created invites"
285 def invites(conn, _params) do
286 invites = UserInviteToken.list_invites()
287
288 conn
289 |> json(AccountView.render("invites.json", %{invites: invites}))
290 end
291
292 @doc "Revokes invite by token"
293 def revoke_invite(conn, %{"token" => token}) do
294 invite = UserInviteToken.find_by_token!(token)
295 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true})
296
297 conn
298 |> json(AccountView.render("invite.json", %{invite: updated_invite}))
299 end
300
301 @doc "Get a password reset token (base64 string) for given nickname"
302 def get_password_reset(conn, %{"nickname" => nickname}) do
303 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
304 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
305
306 conn
307 |> json(token.token)
308 end
309
310 def list_reports(conn, params) do
311 params =
312 params
313 |> Map.put("type", "Flag")
314 |> Map.put("skip_preload", true)
315
316 reports =
317 []
318 |> ActivityPub.fetch_activities(params)
319 |> Enum.reverse()
320
321 conn
322 |> put_view(ReportView)
323 |> render("index.json", %{reports: reports})
324 end
325
326 def report_show(conn, %{"id" => id}) do
327 with %Activity{} = report <- Activity.get_by_id(id) do
328 conn
329 |> put_view(ReportView)
330 |> render("show.json", %{report: report})
331 else
332 _ -> {:error, :not_found}
333 end
334 end
335
336 def report_update_state(conn, %{"id" => id, "state" => state}) do
337 with {:ok, report} <- CommonAPI.update_report_state(id, state) do
338 conn
339 |> put_view(ReportView)
340 |> render("show.json", %{report: report})
341 end
342 end
343
344 def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
345 with false <- is_nil(params["status"]),
346 %Activity{} <- Activity.get_by_id(id) do
347 params =
348 params
349 |> Map.put("in_reply_to_status_id", id)
350 |> Map.put("visibility", "direct")
351
352 {:ok, activity} = CommonAPI.post(user, params)
353
354 conn
355 |> put_view(StatusView)
356 |> render("status.json", %{activity: activity})
357 else
358 true ->
359 {:param_cast, nil}
360
361 nil ->
362 {:error, :not_found}
363 end
364 end
365
366 def status_update(conn, %{"id" => id} = params) do
367 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
368 conn
369 |> put_view(StatusView)
370 |> render("status.json", %{activity: activity})
371 end
372 end
373
374 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
375 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
376 json(conn, %{})
377 end
378 end
379
380 def config_show(conn, _params) do
381 configs = Pleroma.Repo.all(Config)
382
383 conn
384 |> put_view(ConfigView)
385 |> render("index.json", %{configs: configs})
386 end
387
388 def config_update(conn, %{"configs" => configs}) do
389 updated =
390 if Pleroma.Config.get([:instance, :dynamic_configuration]) do
391 updated =
392 Enum.map(configs, fn
393 %{"group" => group, "key" => key, "delete" => "true"} ->
394 {:ok, _} = Config.delete(%{group: group, key: key})
395 nil
396
397 %{"group" => group, "key" => key, "value" => value} ->
398 {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
399 config
400 end)
401 |> Enum.reject(&is_nil(&1))
402
403 Pleroma.Config.TransferTask.load_and_update_env()
404 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
405 updated
406 else
407 []
408 end
409
410 conn
411 |> put_view(ConfigView)
412 |> render("index.json", %{configs: updated})
413 end
414
415 def errors(conn, {:error, :not_found}) do
416 conn
417 |> put_status(:not_found)
418 |> json(dgettext("errors", "Not found"))
419 end
420
421 def errors(conn, {:error, reason}) do
422 conn
423 |> put_status(:bad_request)
424 |> json(reason)
425 end
426
427 def errors(conn, {:param_cast, _}) do
428 conn
429 |> put_status(:bad_request)
430 |> json(dgettext("errors", "Invalid parameters"))
431 end
432
433 def errors(conn, _) do
434 conn
435 |> put_status(:internal_server_error)
436 |> json(dgettext("errors", "Something went wrong"))
437 end
438
439 defp page_params(params) do
440 {get_page(params["page"]), get_page_size(params["page_size"])}
441 end
442
443 defp get_page(page_string) when is_nil(page_string), do: 1
444
445 defp get_page(page_string) do
446 case Integer.parse(page_string) do
447 {page, _} -> page
448 :error -> 1
449 end
450 end
451
452 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
453
454 defp get_page_size(page_size_string) do
455 case Integer.parse(page_size_string) do
456 {page_size, _} -> page_size
457 :error -> @users_page_size
458 end
459 end
460 end