9c5fbfc5dad00ce946e959b7497045e0a06b8234
[akkoma] / lib / pleroma / web / admin_api / 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, only: [json_response: 3]
9
10 alias Pleroma.Activity
11 alias Pleroma.Config
12 alias Pleroma.ConfigDB
13 alias Pleroma.MFA
14 alias Pleroma.ModerationLog
15 alias Pleroma.Plugs.OAuthScopesPlug
16 alias Pleroma.ReportNote
17 alias Pleroma.Stats
18 alias Pleroma.User
19 alias Pleroma.UserInviteToken
20 alias Pleroma.Web.ActivityPub.ActivityPub
21 alias Pleroma.Web.ActivityPub.Builder
22 alias Pleroma.Web.ActivityPub.Pipeline
23 alias Pleroma.Web.ActivityPub.Relay
24 alias Pleroma.Web.ActivityPub.Utils
25 alias Pleroma.Web.AdminAPI.AccountView
26 alias Pleroma.Web.AdminAPI.ConfigView
27 alias Pleroma.Web.AdminAPI.ModerationLogView
28 alias Pleroma.Web.AdminAPI.Report
29 alias Pleroma.Web.AdminAPI.ReportView
30 alias Pleroma.Web.AdminAPI.Search
31 alias Pleroma.Web.CommonAPI
32 alias Pleroma.Web.Endpoint
33 alias Pleroma.Web.MastodonAPI.AppView
34 alias Pleroma.Web.MastodonAPI.StatusView
35 alias Pleroma.Web.OAuth.App
36 alias Pleroma.Web.Router
37
38 require Logger
39
40 @descriptions_json Pleroma.Docs.JSON.compile()
41 @users_page_size 50
42
43 plug(
44 OAuthScopesPlug,
45 %{scopes: ["read:accounts"], admin: true}
46 when action in [:list_users, :user_show, :right_get, :show_user_credentials]
47 )
48
49 plug(
50 OAuthScopesPlug,
51 %{scopes: ["write:accounts"], admin: true}
52 when action in [
53 :get_password_reset,
54 :force_password_reset,
55 :user_delete,
56 :users_create,
57 :user_toggle_activation,
58 :user_activate,
59 :user_deactivate,
60 :tag_users,
61 :untag_users,
62 :right_add,
63 :right_add_multiple,
64 :right_delete,
65 :disable_mfa,
66 :right_delete_multiple,
67 :update_user_credentials
68 ]
69 )
70
71 plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
72
73 plug(
74 OAuthScopesPlug,
75 %{scopes: ["write:invites"], admin: true}
76 when action in [:create_invite_token, :revoke_invite, :email_invite]
77 )
78
79 plug(
80 OAuthScopesPlug,
81 %{scopes: ["write:follows"], admin: true}
82 when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
83 )
84
85 plug(
86 OAuthScopesPlug,
87 %{scopes: ["read:reports"], admin: true}
88 when action in [:list_reports, :report_show]
89 )
90
91 plug(
92 OAuthScopesPlug,
93 %{scopes: ["write:reports"], admin: true}
94 when action in [:reports_update, :report_notes_create, :report_notes_delete]
95 )
96
97 plug(
98 OAuthScopesPlug,
99 %{scopes: ["read:statuses"], admin: true}
100 when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show]
101 )
102
103 plug(
104 OAuthScopesPlug,
105 %{scopes: ["write:statuses"], admin: true}
106 when action in [:status_update, :status_delete]
107 )
108
109 plug(
110 OAuthScopesPlug,
111 %{scopes: ["read"], admin: true}
112 when action in [
113 :config_show,
114 :list_log,
115 :stats,
116 :relay_list,
117 :config_descriptions,
118 :need_reboot
119 ]
120 )
121
122 plug(
123 OAuthScopesPlug,
124 %{scopes: ["write"], admin: true}
125 when action in [
126 :restart,
127 :config_update,
128 :resend_confirmation_email,
129 :confirm_email,
130 :oauth_app_create,
131 :oauth_app_list,
132 :oauth_app_update,
133 :oauth_app_delete,
134 :reload_emoji
135 ]
136 )
137
138 action_fallback(:errors)
139
140 def user_delete(conn, %{"nickname" => nickname}) do
141 user_delete(conn, %{"nicknames" => [nickname]})
142 end
143
144 def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
145 users =
146 nicknames
147 |> Enum.map(&User.get_cached_by_nickname/1)
148
149 users
150 |> Enum.each(fn user ->
151 {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
152 Pipeline.common_pipeline(delete_data, local: true)
153 end)
154
155 ModerationLog.insert_log(%{
156 actor: admin,
157 subject: users,
158 action: "delete"
159 })
160
161 conn
162 |> json(nicknames)
163 end
164
165 def user_follow(%{assigns: %{user: admin}} = conn, %{
166 "follower" => follower_nick,
167 "followed" => followed_nick
168 }) do
169 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
170 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
171 User.follow(follower, followed)
172
173 ModerationLog.insert_log(%{
174 actor: admin,
175 followed: followed,
176 follower: follower,
177 action: "follow"
178 })
179 end
180
181 conn
182 |> json("ok")
183 end
184
185 def user_unfollow(%{assigns: %{user: admin}} = conn, %{
186 "follower" => follower_nick,
187 "followed" => followed_nick
188 }) do
189 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
190 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
191 User.unfollow(follower, followed)
192
193 ModerationLog.insert_log(%{
194 actor: admin,
195 followed: followed,
196 follower: follower,
197 action: "unfollow"
198 })
199 end
200
201 conn
202 |> json("ok")
203 end
204
205 def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
206 changesets =
207 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
208 user_data = %{
209 nickname: nickname,
210 name: nickname,
211 email: email,
212 password: password,
213 password_confirmation: password,
214 bio: "."
215 }
216
217 User.register_changeset(%User{}, user_data, need_confirmation: false)
218 end)
219 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
220 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
221 end)
222
223 case Pleroma.Repo.transaction(changesets) do
224 {:ok, users} ->
225 res =
226 users
227 |> Map.values()
228 |> Enum.map(fn user ->
229 {:ok, user} = User.post_register_action(user)
230
231 user
232 end)
233 |> Enum.map(&AccountView.render("created.json", %{user: &1}))
234
235 ModerationLog.insert_log(%{
236 actor: admin,
237 subjects: Map.values(users),
238 action: "create"
239 })
240
241 conn
242 |> json(res)
243
244 {:error, id, changeset, _} ->
245 res =
246 Enum.map(changesets.operations, fn
247 {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
248 AccountView.render("create-error.json", %{changeset: changeset})
249
250 {_, {:changeset, current_changeset, _}} ->
251 AccountView.render("create-error.json", %{changeset: current_changeset})
252 end)
253
254 conn
255 |> put_status(:conflict)
256 |> json(res)
257 end
258 end
259
260 def user_show(conn, %{"nickname" => nickname}) do
261 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
262 conn
263 |> put_view(AccountView)
264 |> render("show.json", %{user: user})
265 else
266 _ -> {:error, :not_found}
267 end
268 end
269
270 def list_instance_statuses(conn, %{"instance" => instance} = params) do
271 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
272 {page, page_size} = page_params(params)
273
274 activities =
275 ActivityPub.fetch_statuses(nil, %{
276 "instance" => instance,
277 "limit" => page_size,
278 "offset" => (page - 1) * page_size,
279 "exclude_reblogs" => !with_reblogs && "true"
280 })
281
282 conn
283 |> put_view(Pleroma.Web.AdminAPI.StatusView)
284 |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
285 end
286
287 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
288 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
289 godmode = params["godmode"] == "true" || params["godmode"] == true
290
291 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
292 {_, page_size} = page_params(params)
293
294 activities =
295 ActivityPub.fetch_user_activities(user, nil, %{
296 "limit" => page_size,
297 "godmode" => godmode,
298 "exclude_reblogs" => !with_reblogs && "true"
299 })
300
301 conn
302 |> put_view(StatusView)
303 |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
304 else
305 _ -> {:error, :not_found}
306 end
307 end
308
309 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
310 user = User.get_cached_by_nickname(nickname)
311
312 {:ok, updated_user} = User.deactivate(user, !user.deactivated)
313
314 action = if user.deactivated, do: "activate", else: "deactivate"
315
316 ModerationLog.insert_log(%{
317 actor: admin,
318 subject: [user],
319 action: action
320 })
321
322 conn
323 |> put_view(AccountView)
324 |> render("show.json", %{user: updated_user})
325 end
326
327 def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
328 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
329 {:ok, updated_users} = User.deactivate(users, false)
330
331 ModerationLog.insert_log(%{
332 actor: admin,
333 subject: users,
334 action: "activate"
335 })
336
337 conn
338 |> put_view(AccountView)
339 |> render("index.json", %{users: Keyword.values(updated_users)})
340 end
341
342 def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
343 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
344 {:ok, updated_users} = User.deactivate(users, true)
345
346 ModerationLog.insert_log(%{
347 actor: admin,
348 subject: users,
349 action: "deactivate"
350 })
351
352 conn
353 |> put_view(AccountView)
354 |> render("index.json", %{users: Keyword.values(updated_users)})
355 end
356
357 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
358 with {:ok, _} <- User.tag(nicknames, tags) do
359 ModerationLog.insert_log(%{
360 actor: admin,
361 nicknames: nicknames,
362 tags: tags,
363 action: "tag"
364 })
365
366 json_response(conn, :no_content, "")
367 end
368 end
369
370 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
371 with {:ok, _} <- User.untag(nicknames, tags) do
372 ModerationLog.insert_log(%{
373 actor: admin,
374 nicknames: nicknames,
375 tags: tags,
376 action: "untag"
377 })
378
379 json_response(conn, :no_content, "")
380 end
381 end
382
383 def list_users(conn, params) do
384 {page, page_size} = page_params(params)
385 filters = maybe_parse_filters(params["filters"])
386
387 search_params = %{
388 query: params["query"],
389 page: page,
390 page_size: page_size,
391 tags: params["tags"],
392 name: params["name"],
393 email: params["email"]
394 }
395
396 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
397 json(
398 conn,
399 AccountView.render("index.json", users: users, count: count, page_size: page_size)
400 )
401 end
402 end
403
404 @filters ~w(local external active deactivated is_admin is_moderator)
405
406 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
407 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
408
409 defp maybe_parse_filters(filters) do
410 filters
411 |> String.split(",")
412 |> Enum.filter(&Enum.member?(@filters, &1))
413 |> Enum.map(&String.to_atom(&1))
414 |> Enum.into(%{}, &{&1, true})
415 end
416
417 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
418 "permission_group" => permission_group,
419 "nicknames" => nicknames
420 })
421 when permission_group in ["moderator", "admin"] do
422 update = %{:"is_#{permission_group}" => true}
423
424 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
425
426 for u <- users, do: User.admin_api_update(u, update)
427
428 ModerationLog.insert_log(%{
429 action: "grant",
430 actor: admin,
431 subject: users,
432 permission: permission_group
433 })
434
435 json(conn, update)
436 end
437
438 def right_add_multiple(conn, _) do
439 render_error(conn, :not_found, "No such permission_group")
440 end
441
442 def right_add(%{assigns: %{user: admin}} = conn, %{
443 "permission_group" => permission_group,
444 "nickname" => nickname
445 })
446 when permission_group in ["moderator", "admin"] do
447 fields = %{:"is_#{permission_group}" => true}
448
449 {:ok, user} =
450 nickname
451 |> User.get_cached_by_nickname()
452 |> User.admin_api_update(fields)
453
454 ModerationLog.insert_log(%{
455 action: "grant",
456 actor: admin,
457 subject: [user],
458 permission: permission_group
459 })
460
461 json(conn, fields)
462 end
463
464 def right_add(conn, _) do
465 render_error(conn, :not_found, "No such permission_group")
466 end
467
468 def right_get(conn, %{"nickname" => nickname}) do
469 user = User.get_cached_by_nickname(nickname)
470
471 conn
472 |> json(%{
473 is_moderator: user.is_moderator,
474 is_admin: user.is_admin
475 })
476 end
477
478 def right_delete_multiple(
479 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
480 %{
481 "permission_group" => permission_group,
482 "nicknames" => nicknames
483 }
484 )
485 when permission_group in ["moderator", "admin"] do
486 with false <- Enum.member?(nicknames, admin_nickname) do
487 update = %{:"is_#{permission_group}" => false}
488
489 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
490
491 for u <- users, do: User.admin_api_update(u, update)
492
493 ModerationLog.insert_log(%{
494 action: "revoke",
495 actor: admin,
496 subject: users,
497 permission: permission_group
498 })
499
500 json(conn, update)
501 else
502 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
503 end
504 end
505
506 def right_delete_multiple(conn, _) do
507 render_error(conn, :not_found, "No such permission_group")
508 end
509
510 def right_delete(
511 %{assigns: %{user: admin}} = conn,
512 %{
513 "permission_group" => permission_group,
514 "nickname" => nickname
515 }
516 )
517 when permission_group in ["moderator", "admin"] do
518 fields = %{:"is_#{permission_group}" => false}
519
520 {:ok, user} =
521 nickname
522 |> User.get_cached_by_nickname()
523 |> User.admin_api_update(fields)
524
525 ModerationLog.insert_log(%{
526 action: "revoke",
527 actor: admin,
528 subject: [user],
529 permission: permission_group
530 })
531
532 json(conn, fields)
533 end
534
535 def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
536 render_error(conn, :forbidden, "You can't revoke your own admin status.")
537 end
538
539 def relay_list(conn, _params) do
540 with {:ok, list} <- Relay.list() do
541 json(conn, %{relays: list})
542 else
543 _ ->
544 conn
545 |> put_status(500)
546 end
547 end
548
549 def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
550 with {:ok, _message} <- Relay.follow(target) do
551 ModerationLog.insert_log(%{
552 action: "relay_follow",
553 actor: admin,
554 target: target
555 })
556
557 json(conn, target)
558 else
559 _ ->
560 conn
561 |> put_status(500)
562 |> json(target)
563 end
564 end
565
566 def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
567 with {:ok, _message} <- Relay.unfollow(target) do
568 ModerationLog.insert_log(%{
569 action: "relay_unfollow",
570 actor: admin,
571 target: target
572 })
573
574 json(conn, target)
575 else
576 _ ->
577 conn
578 |> put_status(500)
579 |> json(target)
580 end
581 end
582
583 @doc "Sends registration invite via email"
584 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
585 with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
586 {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
587 {:ok, invite_token} <- UserInviteToken.create_invite(),
588 email <-
589 Pleroma.Emails.UserEmail.user_invitation_email(
590 user,
591 invite_token,
592 email,
593 params["name"]
594 ),
595 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
596 json_response(conn, :no_content, "")
597 else
598 {:registrations_open, _} ->
599 errors(
600 conn,
601 {:error, "To send invites you need to set the `registrations_open` option to false."}
602 )
603
604 {:invites_enabled, _} ->
605 errors(
606 conn,
607 {:error, "To send invites you need to set the `invites_enabled` option to true."}
608 )
609 end
610 end
611
612 @doc "Create an account registration invite token"
613 def create_invite_token(conn, params) do
614 opts = %{}
615
616 opts =
617 if params["max_use"],
618 do: Map.put(opts, :max_use, params["max_use"]),
619 else: opts
620
621 opts =
622 if params["expires_at"],
623 do: Map.put(opts, :expires_at, params["expires_at"]),
624 else: opts
625
626 {:ok, invite} = UserInviteToken.create_invite(opts)
627
628 json(conn, AccountView.render("invite.json", %{invite: invite}))
629 end
630
631 @doc "Get list of created invites"
632 def invites(conn, _params) do
633 invites = UserInviteToken.list_invites()
634
635 conn
636 |> put_view(AccountView)
637 |> render("invites.json", %{invites: invites})
638 end
639
640 @doc "Revokes invite by token"
641 def revoke_invite(conn, %{"token" => token}) do
642 with {:ok, invite} <- UserInviteToken.find_by_token(token),
643 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
644 conn
645 |> put_view(AccountView)
646 |> render("invite.json", %{invite: updated_invite})
647 else
648 nil -> {:error, :not_found}
649 end
650 end
651
652 @doc "Get a password reset token (base64 string) for given nickname"
653 def get_password_reset(conn, %{"nickname" => nickname}) do
654 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
655 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
656
657 conn
658 |> json(%{
659 token: token.token,
660 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
661 })
662 end
663
664 @doc "Force password reset for a given user"
665 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
666 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
667
668 Enum.each(users, &User.force_password_reset_async/1)
669
670 ModerationLog.insert_log(%{
671 actor: admin,
672 subject: users,
673 action: "force_password_reset"
674 })
675
676 json_response(conn, :no_content, "")
677 end
678
679 @doc "Disable mfa for user's account."
680 def disable_mfa(conn, %{"nickname" => nickname}) do
681 case User.get_by_nickname(nickname) do
682 %User{} = user ->
683 MFA.disable(user)
684 json(conn, nickname)
685
686 _ ->
687 {:error, :not_found}
688 end
689 end
690
691 @doc "Show a given user's credentials"
692 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
693 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
694 conn
695 |> put_view(AccountView)
696 |> render("credentials.json", %{user: user, for: admin})
697 else
698 _ -> {:error, :not_found}
699 end
700 end
701
702 @doc "Updates a given user"
703 def update_user_credentials(
704 %{assigns: %{user: admin}} = conn,
705 %{"nickname" => nickname} = params
706 ) do
707 with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
708 {:ok, _user} <-
709 User.update_as_admin(user, params) do
710 ModerationLog.insert_log(%{
711 actor: admin,
712 subject: [user],
713 action: "updated_users"
714 })
715
716 if params["password"] do
717 User.force_password_reset_async(user)
718 end
719
720 ModerationLog.insert_log(%{
721 actor: admin,
722 subject: [user],
723 action: "force_password_reset"
724 })
725
726 json(conn, %{status: "success"})
727 else
728 {:error, changeset} ->
729 {_, {error, _}} = Enum.at(changeset.errors, 0)
730 json(conn, %{error: "New password #{error}."})
731
732 _ ->
733 json(conn, %{error: "Unable to change password."})
734 end
735 end
736
737 def list_reports(conn, params) do
738 {page, page_size} = page_params(params)
739
740 reports = Utils.get_reports(params, page, page_size)
741
742 conn
743 |> put_view(ReportView)
744 |> render("index.json", %{reports: reports})
745 end
746
747 def report_show(conn, %{"id" => id}) do
748 with %Activity{} = report <- Activity.get_by_id(id) do
749 conn
750 |> put_view(ReportView)
751 |> render("show.json", Report.extract_report_info(report))
752 else
753 _ -> {:error, :not_found}
754 end
755 end
756
757 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
758 result =
759 reports
760 |> Enum.map(fn report ->
761 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
762 ModerationLog.insert_log(%{
763 action: "report_update",
764 actor: admin,
765 subject: activity
766 })
767
768 activity
769 else
770 {:error, message} -> %{id: report["id"], error: message}
771 end
772 end)
773
774 case Enum.any?(result, &Map.has_key?(&1, :error)) do
775 true -> json_response(conn, :bad_request, result)
776 false -> json_response(conn, :no_content, "")
777 end
778 end
779
780 def report_notes_create(%{assigns: %{user: user}} = conn, %{
781 "id" => report_id,
782 "content" => content
783 }) do
784 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
785 ModerationLog.insert_log(%{
786 action: "report_note",
787 actor: user,
788 subject: Activity.get_by_id(report_id),
789 text: content
790 })
791
792 json_response(conn, :no_content, "")
793 else
794 _ -> json_response(conn, :bad_request, "")
795 end
796 end
797
798 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
799 "id" => note_id,
800 "report_id" => report_id
801 }) do
802 with {:ok, note} <- ReportNote.destroy(note_id) do
803 ModerationLog.insert_log(%{
804 action: "report_note_delete",
805 actor: user,
806 subject: Activity.get_by_id(report_id),
807 text: note.content
808 })
809
810 json_response(conn, :no_content, "")
811 else
812 _ -> json_response(conn, :bad_request, "")
813 end
814 end
815
816 def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
817 godmode = params["godmode"] == "true" || params["godmode"] == true
818 local_only = params["local_only"] == "true" || params["local_only"] == true
819 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
820 {page, page_size} = page_params(params)
821
822 activities =
823 ActivityPub.fetch_statuses(nil, %{
824 "godmode" => godmode,
825 "local_only" => local_only,
826 "limit" => page_size,
827 "offset" => (page - 1) * page_size,
828 "exclude_reblogs" => !with_reblogs && "true"
829 })
830
831 conn
832 |> put_view(Pleroma.Web.AdminAPI.StatusView)
833 |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
834 end
835
836 def status_show(conn, %{"id" => id}) do
837 with %Activity{} = activity <- Activity.get_by_id(id) do
838 conn
839 |> put_view(StatusView)
840 |> render("show.json", %{activity: activity})
841 else
842 _ -> errors(conn, {:error, :not_found})
843 end
844 end
845
846 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
847 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
848 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
849
850 ModerationLog.insert_log(%{
851 action: "status_update",
852 actor: admin,
853 subject: activity,
854 sensitive: sensitive,
855 visibility: params["visibility"]
856 })
857
858 conn
859 |> put_view(StatusView)
860 |> render("show.json", %{activity: activity})
861 end
862 end
863
864 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
865 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
866 ModerationLog.insert_log(%{
867 action: "status_delete",
868 actor: user,
869 subject_id: id
870 })
871
872 json(conn, %{})
873 end
874 end
875
876 def list_log(conn, params) do
877 {page, page_size} = page_params(params)
878
879 log =
880 ModerationLog.get_all(%{
881 page: page,
882 page_size: page_size,
883 start_date: params["start_date"],
884 end_date: params["end_date"],
885 user_id: params["user_id"],
886 search: params["search"]
887 })
888
889 conn
890 |> put_view(ModerationLogView)
891 |> render("index.json", %{log: log})
892 end
893
894 def config_descriptions(conn, _params) do
895 conn
896 |> Plug.Conn.put_resp_content_type("application/json")
897 |> Plug.Conn.send_resp(200, @descriptions_json)
898 end
899
900 def config_show(conn, %{"only_db" => true}) do
901 with :ok <- configurable_from_database(conn) do
902 configs = Pleroma.Repo.all(ConfigDB)
903
904 conn
905 |> put_view(ConfigView)
906 |> render("index.json", %{configs: configs})
907 end
908 end
909
910 def config_show(conn, _params) do
911 with :ok <- configurable_from_database(conn) do
912 configs = ConfigDB.get_all_as_keyword()
913
914 merged =
915 Config.Holder.default_config()
916 |> ConfigDB.merge(configs)
917 |> Enum.map(fn {group, values} ->
918 Enum.map(values, fn {key, value} ->
919 db =
920 if configs[group][key] do
921 ConfigDB.get_db_keys(configs[group][key], key)
922 end
923
924 db_value = configs[group][key]
925
926 merged_value =
927 if !is_nil(db_value) and Keyword.keyword?(db_value) and
928 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
929 ConfigDB.merge_group(group, key, value, db_value)
930 else
931 value
932 end
933
934 setting = %{
935 group: ConfigDB.convert(group),
936 key: ConfigDB.convert(key),
937 value: ConfigDB.convert(merged_value)
938 }
939
940 if db, do: Map.put(setting, :db, db), else: setting
941 end)
942 end)
943 |> List.flatten()
944
945 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
946 end
947 end
948
949 def config_update(conn, %{"configs" => configs}) do
950 with :ok <- configurable_from_database(conn) do
951 {_errors, results} =
952 Enum.filter(configs, &whitelisted_config?/1)
953 |> Enum.map(fn
954 %{"group" => group, "key" => key, "delete" => true} = params ->
955 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
956
957 %{"group" => group, "key" => key, "value" => value} ->
958 ConfigDB.update_or_create(%{group: group, key: key, value: value})
959 end)
960 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
961
962 {deleted, updated} =
963 results
964 |> Enum.map(fn {:ok, config} ->
965 Map.put(config, :db, ConfigDB.get_db_keys(config))
966 end)
967 |> Enum.split_with(fn config ->
968 Ecto.get_meta(config, :state) == :deleted
969 end)
970
971 Config.TransferTask.load_and_update_env(deleted, false)
972
973 if !Restarter.Pleroma.need_reboot?() do
974 changed_reboot_settings? =
975 (updated ++ deleted)
976 |> Enum.any?(fn config ->
977 group = ConfigDB.from_string(config.group)
978 key = ConfigDB.from_string(config.key)
979 value = ConfigDB.from_binary(config.value)
980 Config.TransferTask.pleroma_need_restart?(group, key, value)
981 end)
982
983 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
984 end
985
986 conn
987 |> put_view(ConfigView)
988 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
989 end
990 end
991
992 def restart(conn, _params) do
993 with :ok <- configurable_from_database(conn) do
994 Restarter.Pleroma.restart(Config.get(:env), 50)
995
996 json(conn, %{})
997 end
998 end
999
1000 def need_reboot(conn, _params) do
1001 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
1002 end
1003
1004 defp configurable_from_database(conn) do
1005 if Config.get(:configurable_from_database) do
1006 :ok
1007 else
1008 errors(
1009 conn,
1010 {:error, "To use this endpoint you need to enable configuration from database."}
1011 )
1012 end
1013 end
1014
1015 defp whitelisted_config?(%{"group" => group, "key" => key}) do
1016 if whitelisted_configs = Config.get(:database_config_whitelist) do
1017 Enum.any?(whitelisted_configs, fn {whitelisted_group, whitelisted_key} ->
1018 group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
1019 end)
1020 else
1021 true
1022 end
1023 end
1024
1025 def reload_emoji(conn, _params) do
1026 Pleroma.Emoji.reload()
1027
1028 conn |> json("ok")
1029 end
1030
1031 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1032 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1033
1034 User.toggle_confirmation(users)
1035
1036 ModerationLog.insert_log(%{
1037 actor: admin,
1038 subject: users,
1039 action: "confirm_email"
1040 })
1041
1042 conn |> json("")
1043 end
1044
1045 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1046 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1047
1048 User.try_send_confirmation_email(users)
1049
1050 ModerationLog.insert_log(%{
1051 actor: admin,
1052 subject: users,
1053 action: "resend_confirmation_email"
1054 })
1055
1056 conn |> json("")
1057 end
1058
1059 def oauth_app_create(conn, params) do
1060 params =
1061 if params["name"] do
1062 Map.put(params, "client_name", params["name"])
1063 else
1064 params
1065 end
1066
1067 result =
1068 case App.create(params) do
1069 {:ok, app} ->
1070 AppView.render("show.json", %{app: app, admin: true})
1071
1072 {:error, changeset} ->
1073 App.errors(changeset)
1074 end
1075
1076 json(conn, result)
1077 end
1078
1079 def oauth_app_update(conn, params) do
1080 params =
1081 if params["name"] do
1082 Map.put(params, "client_name", params["name"])
1083 else
1084 params
1085 end
1086
1087 with {:ok, app} <- App.update(params) do
1088 json(conn, AppView.render("show.json", %{app: app, admin: true}))
1089 else
1090 {:error, changeset} ->
1091 json(conn, App.errors(changeset))
1092
1093 nil ->
1094 json_response(conn, :bad_request, "")
1095 end
1096 end
1097
1098 def oauth_app_list(conn, params) do
1099 {page, page_size} = page_params(params)
1100
1101 search_params = %{
1102 client_name: params["name"],
1103 client_id: params["client_id"],
1104 page: page,
1105 page_size: page_size
1106 }
1107
1108 search_params =
1109 if Map.has_key?(params, "trusted") do
1110 Map.put(search_params, :trusted, params["trusted"])
1111 else
1112 search_params
1113 end
1114
1115 with {:ok, apps, count} <- App.search(search_params) do
1116 json(
1117 conn,
1118 AppView.render("index.json",
1119 apps: apps,
1120 count: count,
1121 page_size: page_size,
1122 admin: true
1123 )
1124 )
1125 end
1126 end
1127
1128 def oauth_app_delete(conn, params) do
1129 with {:ok, _app} <- App.destroy(params["id"]) do
1130 json_response(conn, :no_content, "")
1131 else
1132 _ -> json_response(conn, :bad_request, "")
1133 end
1134 end
1135
1136 def stats(conn, _) do
1137 count = Stats.get_status_visibility_count()
1138
1139 conn
1140 |> json(%{"status_visibility" => count})
1141 end
1142
1143 defp errors(conn, {:error, :not_found}) do
1144 conn
1145 |> put_status(:not_found)
1146 |> json(dgettext("errors", "Not found"))
1147 end
1148
1149 defp errors(conn, {:error, reason}) do
1150 conn
1151 |> put_status(:bad_request)
1152 |> json(reason)
1153 end
1154
1155 defp errors(conn, {:param_cast, _}) do
1156 conn
1157 |> put_status(:bad_request)
1158 |> json(dgettext("errors", "Invalid parameters"))
1159 end
1160
1161 defp errors(conn, _) do
1162 conn
1163 |> put_status(:internal_server_error)
1164 |> json(dgettext("errors", "Something went wrong"))
1165 end
1166
1167 defp page_params(params) do
1168 {get_page(params["page"]), get_page_size(params["page_size"])}
1169 end
1170
1171 defp get_page(page_string) when is_nil(page_string), do: 1
1172
1173 defp get_page(page_string) do
1174 case Integer.parse(page_string) do
1175 {page, _} -> page
1176 :error -> 1
1177 end
1178 end
1179
1180 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1181
1182 defp get_page_size(page_size_string) do
1183 case Integer.parse(page_size_string) do
1184 {page_size, _} -> page_size
1185 :error -> @users_page_size
1186 end
1187 end
1188 end