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