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