1894000ff492b0ed38866a6d26849107eab14be7
[anni] / lib / pleroma / web / admin_api / controllers / admin_api_controller.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.AdminAPI.AdminAPIController do
6   use Pleroma.Web, :controller
7
8   import Pleroma.Web.ControllerHelper,
9     only: [json_response: 3, fetch_integer_param: 3]
10
11   alias Pleroma.Config
12   alias Pleroma.MFA
13   alias Pleroma.ModerationLog
14   alias Pleroma.Stats
15   alias Pleroma.User
16   alias Pleroma.Web.ActivityPub.ActivityPub
17   alias Pleroma.Web.AdminAPI
18   alias Pleroma.Web.AdminAPI.AccountView
19   alias Pleroma.Web.AdminAPI.ModerationLogView
20   alias Pleroma.Web.Endpoint
21   alias Pleroma.Web.Plugs.OAuthScopesPlug
22   alias Pleroma.Web.Router
23
24   @users_page_size 50
25
26   plug(
27     OAuthScopesPlug,
28     %{scopes: ["admin:read:accounts"]}
29     when action in [:right_get, :show_user_credentials, :create_backup]
30   )
31
32   plug(
33     OAuthScopesPlug,
34     %{scopes: ["admin:write:accounts"]}
35     when action in [
36            :get_password_reset,
37            :force_password_reset,
38            :tag_users,
39            :untag_users,
40            :right_add,
41            :right_add_multiple,
42            :right_delete,
43            :disable_mfa,
44            :right_delete_multiple,
45            :update_user_credentials
46          ]
47   )
48
49   plug(
50     OAuthScopesPlug,
51     %{scopes: ["admin:read:statuses"]}
52     when action in [:list_user_statuses]
53   )
54
55   plug(
56     OAuthScopesPlug,
57     %{scopes: ["admin:read:chats"]}
58     when action in [:list_user_chats]
59   )
60
61   plug(
62     OAuthScopesPlug,
63     %{scopes: ["admin:read"]}
64     when action in [
65            :list_log,
66            :stats,
67            :need_reboot
68          ]
69   )
70
71   plug(
72     OAuthScopesPlug,
73     %{scopes: ["admin:write"]}
74     when action in [
75            :restart,
76            :resend_confirmation_email,
77            :confirm_email,
78            :reload_emoji
79          ]
80   )
81
82   action_fallback(AdminAPI.FallbackController)
83
84   def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do
85     with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
86     godmode = params["godmode"] == "true" || params["godmode"] == true
87
88     with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
89       {page, page_size} = page_params(params)
90
91       result =
92         ActivityPub.fetch_user_activities(user, nil, %{
93           limit: page_size,
94           offset: (page - 1) * page_size,
95           godmode: godmode,
96           exclude_reblogs: not with_reblogs,
97           pagination_type: :offset,
98           total: true
99         })
100
101       conn
102       |> put_view(AdminAPI.StatusView)
103       |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
104     else
105       _ -> {:error, :not_found}
106     end
107   end
108
109   def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do
110     with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
111       chats =
112         Pleroma.Chat.for_user_query(user_id)
113         |> Pleroma.Repo.all()
114
115       conn
116       |> put_view(AdminAPI.ChatView)
117       |> render("index.json", chats: chats)
118     else
119       _ -> {:error, :not_found}
120     end
121   end
122
123   def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
124     with {:ok, _} <- User.tag(nicknames, tags) do
125       ModerationLog.insert_log(%{
126         actor: admin,
127         nicknames: nicknames,
128         tags: tags,
129         action: "tag"
130       })
131
132       json_response(conn, :no_content, "")
133     end
134   end
135
136   def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
137     with {:ok, _} <- User.untag(nicknames, tags) do
138       ModerationLog.insert_log(%{
139         actor: admin,
140         nicknames: nicknames,
141         tags: tags,
142         action: "untag"
143       })
144
145       json_response(conn, :no_content, "")
146     end
147   end
148
149   def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
150         "permission_group" => permission_group,
151         "nicknames" => nicknames
152       })
153       when permission_group in ["moderator", "admin"] do
154     update = %{:"is_#{permission_group}" => true}
155
156     users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
157
158     for u <- users, do: User.admin_api_update(u, update)
159
160     ModerationLog.insert_log(%{
161       action: "grant",
162       actor: admin,
163       subject: users,
164       permission: permission_group
165     })
166
167     json(conn, update)
168   end
169
170   def right_add_multiple(conn, _) do
171     render_error(conn, :not_found, "No such permission_group")
172   end
173
174   def right_add(%{assigns: %{user: admin}} = conn, %{
175         "permission_group" => permission_group,
176         "nickname" => nickname
177       })
178       when permission_group in ["moderator", "admin"] do
179     fields = %{:"is_#{permission_group}" => true}
180
181     {:ok, user} =
182       nickname
183       |> User.get_cached_by_nickname()
184       |> User.admin_api_update(fields)
185
186     ModerationLog.insert_log(%{
187       action: "grant",
188       actor: admin,
189       subject: [user],
190       permission: permission_group
191     })
192
193     json(conn, fields)
194   end
195
196   def right_add(conn, _) do
197     render_error(conn, :not_found, "No such permission_group")
198   end
199
200   def right_get(conn, %{"nickname" => nickname}) do
201     user = User.get_cached_by_nickname(nickname)
202
203     conn
204     |> json(%{
205       is_moderator: user.is_moderator,
206       is_admin: user.is_admin
207     })
208   end
209
210   def right_delete_multiple(
211         %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
212         %{
213           "permission_group" => permission_group,
214           "nicknames" => nicknames
215         }
216       )
217       when permission_group in ["moderator", "admin"] do
218     with false <- Enum.member?(nicknames, admin_nickname) do
219       update = %{:"is_#{permission_group}" => false}
220
221       users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
222
223       for u <- users, do: User.admin_api_update(u, update)
224
225       ModerationLog.insert_log(%{
226         action: "revoke",
227         actor: admin,
228         subject: users,
229         permission: permission_group
230       })
231
232       json(conn, update)
233     else
234       _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
235     end
236   end
237
238   def right_delete_multiple(conn, _) do
239     render_error(conn, :not_found, "No such permission_group")
240   end
241
242   def right_delete(
243         %{assigns: %{user: admin}} = conn,
244         %{
245           "permission_group" => permission_group,
246           "nickname" => nickname
247         }
248       )
249       when permission_group in ["moderator", "admin"] do
250     fields = %{:"is_#{permission_group}" => false}
251
252     {:ok, user} =
253       nickname
254       |> User.get_cached_by_nickname()
255       |> User.admin_api_update(fields)
256
257     ModerationLog.insert_log(%{
258       action: "revoke",
259       actor: admin,
260       subject: [user],
261       permission: permission_group
262     })
263
264     json(conn, fields)
265   end
266
267   def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
268     render_error(conn, :forbidden, "You can't revoke your own admin status.")
269   end
270
271   @doc "Get a password reset token (base64 string) for given nickname"
272   def get_password_reset(conn, %{"nickname" => nickname}) do
273     (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
274     {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
275
276     conn
277     |> json(%{
278       token: token.token,
279       link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
280     })
281   end
282
283   @doc "Force password reset for a given user"
284   def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
285     users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
286
287     Enum.each(users, &User.force_password_reset_async/1)
288
289     ModerationLog.insert_log(%{
290       actor: admin,
291       subject: users,
292       action: "force_password_reset"
293     })
294
295     json_response(conn, :no_content, "")
296   end
297
298   @doc "Disable mfa for user's account."
299   def disable_mfa(conn, %{"nickname" => nickname}) do
300     case User.get_by_nickname(nickname) do
301       %User{} = user ->
302         MFA.disable(user)
303         json(conn, nickname)
304
305       _ ->
306         {:error, :not_found}
307     end
308   end
309
310   @doc "Show a given user's credentials"
311   def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
312     with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
313       conn
314       |> put_view(AccountView)
315       |> render("credentials.json", %{user: user, for: admin})
316     else
317       _ -> {:error, :not_found}
318     end
319   end
320
321   @doc "Updates a given user"
322   def update_user_credentials(
323         %{assigns: %{user: admin}} = conn,
324         %{"nickname" => nickname} = params
325       ) do
326     with {_, %User{} = user} <- {:user, User.get_cached_by_nickname(nickname)},
327          {:ok, _user} <-
328            User.update_as_admin(user, params) do
329       ModerationLog.insert_log(%{
330         actor: admin,
331         subject: [user],
332         action: "updated_users"
333       })
334
335       if params["password"] do
336         User.force_password_reset_async(user)
337       end
338
339       ModerationLog.insert_log(%{
340         actor: admin,
341         subject: [user],
342         action: "force_password_reset"
343       })
344
345       json(conn, %{status: "success"})
346     else
347       {:error, changeset} ->
348         errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
349
350         {:errors, errors}
351
352       _ ->
353         {:error, :not_found}
354     end
355   end
356
357   def list_log(conn, params) do
358     {page, page_size} = page_params(params)
359
360     log =
361       ModerationLog.get_all(%{
362         page: page,
363         page_size: page_size,
364         start_date: params["start_date"],
365         end_date: params["end_date"],
366         user_id: params["user_id"],
367         search: params["search"]
368       })
369
370     conn
371     |> put_view(ModerationLogView)
372     |> render("index.json", %{log: log})
373   end
374
375   def restart(conn, _params) do
376     with :ok <- configurable_from_database() do
377       Restarter.Pleroma.restart(Config.get(:env), 50)
378
379       json(conn, %{})
380     end
381   end
382
383   def need_reboot(conn, _params) do
384     json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
385   end
386
387   defp configurable_from_database do
388     if Config.get(:configurable_from_database) do
389       :ok
390     else
391       {:error, "You must enable configurable_from_database in your config file."}
392     end
393   end
394
395   def reload_emoji(conn, _params) do
396     Pleroma.Emoji.reload()
397
398     json(conn, "ok")
399   end
400
401   def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
402     users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
403
404     User.confirm(users)
405
406     ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"})
407
408     json(conn, "")
409   end
410
411   def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
412     users =
413       Enum.map(nicknames, fn nickname ->
414         nickname
415         |> User.get_cached_by_nickname()
416         |> User.send_confirmation_email()
417       end)
418
419     ModerationLog.insert_log(%{actor: admin, subject: users, action: "resend_confirmation_email"})
420
421     json(conn, "")
422   end
423
424   def stats(conn, params) do
425     counters = Stats.get_status_visibility_count(params["instance"])
426
427     json(conn, %{"status_visibility" => counters})
428   end
429
430   def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
431     with %User{} = user <- User.get_by_nickname(nickname),
432          {:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do
433       ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"})
434
435       json(conn, "")
436     end
437   end
438
439   defp page_params(params) do
440     {
441       fetch_integer_param(params, "page", 1),
442       fetch_integer_param(params, "page_size", @users_page_size)
443     }
444   end
445 end