Merge branch 'feature/database-configuration-whitelist' into 'develop'
[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 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 params =
848 params
849 |> Map.take(["sensitive", "visibility"])
850 |> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end)
851
852 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
853 {:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive])
854
855 ModerationLog.insert_log(%{
856 action: "status_update",
857 actor: admin,
858 subject: activity,
859 sensitive: sensitive,
860 visibility: params[:visibility]
861 })
862
863 conn
864 |> put_view(StatusView)
865 |> render("show.json", %{activity: activity})
866 end
867 end
868
869 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
870 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
871 ModerationLog.insert_log(%{
872 action: "status_delete",
873 actor: user,
874 subject_id: id
875 })
876
877 json(conn, %{})
878 end
879 end
880
881 def list_log(conn, params) do
882 {page, page_size} = page_params(params)
883
884 log =
885 ModerationLog.get_all(%{
886 page: page,
887 page_size: page_size,
888 start_date: params["start_date"],
889 end_date: params["end_date"],
890 user_id: params["user_id"],
891 search: params["search"]
892 })
893
894 conn
895 |> put_view(ModerationLogView)
896 |> render("index.json", %{log: log})
897 end
898
899 def config_descriptions(conn, _params) do
900 descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
901
902 json(conn, descriptions)
903 end
904
905 def config_show(conn, %{"only_db" => true}) do
906 with :ok <- configurable_from_database(conn) do
907 configs = Pleroma.Repo.all(ConfigDB)
908
909 conn
910 |> put_view(ConfigView)
911 |> render("index.json", %{configs: configs})
912 end
913 end
914
915 def config_show(conn, _params) do
916 with :ok <- configurable_from_database(conn) do
917 configs = ConfigDB.get_all_as_keyword()
918
919 merged =
920 Config.Holder.default_config()
921 |> ConfigDB.merge(configs)
922 |> Enum.map(fn {group, values} ->
923 Enum.map(values, fn {key, value} ->
924 db =
925 if configs[group][key] do
926 ConfigDB.get_db_keys(configs[group][key], key)
927 end
928
929 db_value = configs[group][key]
930
931 merged_value =
932 if !is_nil(db_value) and Keyword.keyword?(db_value) and
933 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
934 ConfigDB.merge_group(group, key, value, db_value)
935 else
936 value
937 end
938
939 setting = %{
940 group: ConfigDB.convert(group),
941 key: ConfigDB.convert(key),
942 value: ConfigDB.convert(merged_value)
943 }
944
945 if db, do: Map.put(setting, :db, db), else: setting
946 end)
947 end)
948 |> List.flatten()
949
950 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
951 end
952 end
953
954 def config_update(conn, %{"configs" => configs}) do
955 with :ok <- configurable_from_database(conn) do
956 {_errors, results} =
957 configs
958 |> Enum.filter(&whitelisted_config?/1)
959 |> Enum.map(fn
960 %{"group" => group, "key" => key, "delete" => true} = params ->
961 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
962
963 %{"group" => group, "key" => key, "value" => value} ->
964 ConfigDB.update_or_create(%{group: group, key: key, value: value})
965 end)
966 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
967
968 {deleted, updated} =
969 results
970 |> Enum.map(fn {:ok, config} ->
971 Map.put(config, :db, ConfigDB.get_db_keys(config))
972 end)
973 |> Enum.split_with(fn config ->
974 Ecto.get_meta(config, :state) == :deleted
975 end)
976
977 Config.TransferTask.load_and_update_env(deleted, false)
978
979 if !Restarter.Pleroma.need_reboot?() do
980 changed_reboot_settings? =
981 (updated ++ deleted)
982 |> Enum.any?(fn config ->
983 group = ConfigDB.from_string(config.group)
984 key = ConfigDB.from_string(config.key)
985 value = ConfigDB.from_binary(config.value)
986 Config.TransferTask.pleroma_need_restart?(group, key, value)
987 end)
988
989 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
990 end
991
992 conn
993 |> put_view(ConfigView)
994 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
995 end
996 end
997
998 def restart(conn, _params) do
999 with :ok <- configurable_from_database(conn) do
1000 Restarter.Pleroma.restart(Config.get(:env), 50)
1001
1002 json(conn, %{})
1003 end
1004 end
1005
1006 def need_reboot(conn, _params) do
1007 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
1008 end
1009
1010 defp configurable_from_database(conn) do
1011 if Config.get(:configurable_from_database) do
1012 :ok
1013 else
1014 errors(
1015 conn,
1016 {:error, "To use this endpoint you need to enable configuration from database."}
1017 )
1018 end
1019 end
1020
1021 defp whitelisted_config?(group, key) do
1022 if whitelisted_configs = Config.get(:database_config_whitelist) do
1023 Enum.any?(whitelisted_configs, fn
1024 {whitelisted_group} ->
1025 group == inspect(whitelisted_group)
1026
1027 {whitelisted_group, whitelisted_key} ->
1028 group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
1029 end)
1030 else
1031 true
1032 end
1033 end
1034
1035 defp whitelisted_config?(%{"group" => group, "key" => key}) do
1036 whitelisted_config?(group, key)
1037 end
1038
1039 defp whitelisted_config?(%{:group => group} = config) do
1040 whitelisted_config?(group, config[:key])
1041 end
1042
1043 def reload_emoji(conn, _params) do
1044 Pleroma.Emoji.reload()
1045
1046 conn |> json("ok")
1047 end
1048
1049 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1050 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1051
1052 User.toggle_confirmation(users)
1053
1054 ModerationLog.insert_log(%{
1055 actor: admin,
1056 subject: users,
1057 action: "confirm_email"
1058 })
1059
1060 conn |> json("")
1061 end
1062
1063 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1064 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1065
1066 User.try_send_confirmation_email(users)
1067
1068 ModerationLog.insert_log(%{
1069 actor: admin,
1070 subject: users,
1071 action: "resend_confirmation_email"
1072 })
1073
1074 conn |> json("")
1075 end
1076
1077 def oauth_app_create(conn, params) do
1078 params =
1079 if params["name"] do
1080 Map.put(params, "client_name", params["name"])
1081 else
1082 params
1083 end
1084
1085 result =
1086 case App.create(params) do
1087 {:ok, app} ->
1088 AppView.render("show.json", %{app: app, admin: true})
1089
1090 {:error, changeset} ->
1091 App.errors(changeset)
1092 end
1093
1094 json(conn, result)
1095 end
1096
1097 def oauth_app_update(conn, params) do
1098 params =
1099 if params["name"] do
1100 Map.put(params, "client_name", params["name"])
1101 else
1102 params
1103 end
1104
1105 with {:ok, app} <- App.update(params) do
1106 json(conn, AppView.render("show.json", %{app: app, admin: true}))
1107 else
1108 {:error, changeset} ->
1109 json(conn, App.errors(changeset))
1110
1111 nil ->
1112 json_response(conn, :bad_request, "")
1113 end
1114 end
1115
1116 def oauth_app_list(conn, params) do
1117 {page, page_size} = page_params(params)
1118
1119 search_params = %{
1120 client_name: params["name"],
1121 client_id: params["client_id"],
1122 page: page,
1123 page_size: page_size
1124 }
1125
1126 search_params =
1127 if Map.has_key?(params, "trusted") do
1128 Map.put(search_params, :trusted, params["trusted"])
1129 else
1130 search_params
1131 end
1132
1133 with {:ok, apps, count} <- App.search(search_params) do
1134 json(
1135 conn,
1136 AppView.render("index.json",
1137 apps: apps,
1138 count: count,
1139 page_size: page_size,
1140 admin: true
1141 )
1142 )
1143 end
1144 end
1145
1146 def oauth_app_delete(conn, params) do
1147 with {:ok, _app} <- App.destroy(params["id"]) do
1148 json_response(conn, :no_content, "")
1149 else
1150 _ -> json_response(conn, :bad_request, "")
1151 end
1152 end
1153
1154 def stats(conn, _) do
1155 count = Stats.get_status_visibility_count()
1156
1157 conn
1158 |> json(%{"status_visibility" => count})
1159 end
1160
1161 defp errors(conn, {:error, :not_found}) do
1162 conn
1163 |> put_status(:not_found)
1164 |> json(dgettext("errors", "Not found"))
1165 end
1166
1167 defp errors(conn, {:error, reason}) do
1168 conn
1169 |> put_status(:bad_request)
1170 |> json(reason)
1171 end
1172
1173 defp errors(conn, {:param_cast, _}) do
1174 conn
1175 |> put_status(:bad_request)
1176 |> json(dgettext("errors", "Invalid parameters"))
1177 end
1178
1179 defp errors(conn, _) do
1180 conn
1181 |> put_status(:internal_server_error)
1182 |> json(dgettext("errors", "Something went wrong"))
1183 end
1184
1185 defp page_params(params) do
1186 {get_page(params["page"]), get_page_size(params["page_size"])}
1187 end
1188
1189 defp get_page(page_string) when is_nil(page_string), do: 1
1190
1191 defp get_page(page_string) do
1192 case Integer.parse(page_string) do
1193 {page, _} -> page
1194 :error -> 1
1195 end
1196 end
1197
1198 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1199
1200 defp get_page_size(page_size_string) do
1201 case Integer.parse(page_size_string) do
1202 {page_size, _} -> page_size
1203 :error -> @users_page_size
1204 end
1205 end
1206 end