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