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