Merge remote-tracking branch 'origin/develop' into feature/embeddable-posts
[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 = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
719
720 json(conn, %{errors: errors})
721
722 _ ->
723 json(conn, %{error: "Unable to update user."})
724 end
725 end
726
727 def list_reports(conn, params) do
728 {page, page_size} = page_params(params)
729
730 reports = Utils.get_reports(params, page, page_size)
731
732 conn
733 |> put_view(ReportView)
734 |> render("index.json", %{reports: reports})
735 end
736
737 def report_show(conn, %{"id" => id}) do
738 with %Activity{} = report <- Activity.get_by_id(id) do
739 conn
740 |> put_view(ReportView)
741 |> render("show.json", Report.extract_report_info(report))
742 else
743 _ -> {:error, :not_found}
744 end
745 end
746
747 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
748 result =
749 reports
750 |> Enum.map(fn report ->
751 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
752 ModerationLog.insert_log(%{
753 action: "report_update",
754 actor: admin,
755 subject: activity
756 })
757
758 activity
759 else
760 {:error, message} -> %{id: report["id"], error: message}
761 end
762 end)
763
764 case Enum.any?(result, &Map.has_key?(&1, :error)) do
765 true -> json_response(conn, :bad_request, result)
766 false -> json_response(conn, :no_content, "")
767 end
768 end
769
770 def report_notes_create(%{assigns: %{user: user}} = conn, %{
771 "id" => report_id,
772 "content" => content
773 }) do
774 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
775 ModerationLog.insert_log(%{
776 action: "report_note",
777 actor: user,
778 subject: Activity.get_by_id(report_id),
779 text: content
780 })
781
782 json_response(conn, :no_content, "")
783 else
784 _ -> json_response(conn, :bad_request, "")
785 end
786 end
787
788 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
789 "id" => note_id,
790 "report_id" => report_id
791 }) do
792 with {:ok, note} <- ReportNote.destroy(note_id) do
793 ModerationLog.insert_log(%{
794 action: "report_note_delete",
795 actor: user,
796 subject: Activity.get_by_id(report_id),
797 text: note.content
798 })
799
800 json_response(conn, :no_content, "")
801 else
802 _ -> json_response(conn, :bad_request, "")
803 end
804 end
805
806 def list_log(conn, params) do
807 {page, page_size} = page_params(params)
808
809 log =
810 ModerationLog.get_all(%{
811 page: page,
812 page_size: page_size,
813 start_date: params["start_date"],
814 end_date: params["end_date"],
815 user_id: params["user_id"],
816 search: params["search"]
817 })
818
819 conn
820 |> put_view(ModerationLogView)
821 |> render("index.json", %{log: log})
822 end
823
824 def config_descriptions(conn, _params) do
825 descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
826
827 json(conn, descriptions)
828 end
829
830 def config_show(conn, %{"only_db" => true}) do
831 with :ok <- configurable_from_database() do
832 configs = Pleroma.Repo.all(ConfigDB)
833
834 conn
835 |> put_view(ConfigView)
836 |> render("index.json", %{configs: configs})
837 end
838 end
839
840 def config_show(conn, _params) do
841 with :ok <- configurable_from_database() do
842 configs = ConfigDB.get_all_as_keyword()
843
844 merged =
845 Config.Holder.default_config()
846 |> ConfigDB.merge(configs)
847 |> Enum.map(fn {group, values} ->
848 Enum.map(values, fn {key, value} ->
849 db =
850 if configs[group][key] do
851 ConfigDB.get_db_keys(configs[group][key], key)
852 end
853
854 db_value = configs[group][key]
855
856 merged_value =
857 if !is_nil(db_value) and Keyword.keyword?(db_value) and
858 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
859 ConfigDB.merge_group(group, key, value, db_value)
860 else
861 value
862 end
863
864 setting = %{
865 group: ConfigDB.convert(group),
866 key: ConfigDB.convert(key),
867 value: ConfigDB.convert(merged_value)
868 }
869
870 if db, do: Map.put(setting, :db, db), else: setting
871 end)
872 end)
873 |> List.flatten()
874
875 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
876 end
877 end
878
879 def config_update(conn, %{"configs" => configs}) do
880 with :ok <- configurable_from_database() do
881 {_errors, results} =
882 configs
883 |> Enum.filter(&whitelisted_config?/1)
884 |> Enum.map(fn
885 %{"group" => group, "key" => key, "delete" => true} = params ->
886 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
887
888 %{"group" => group, "key" => key, "value" => value} ->
889 ConfigDB.update_or_create(%{group: group, key: key, value: value})
890 end)
891 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
892
893 {deleted, updated} =
894 results
895 |> Enum.map(fn {:ok, config} ->
896 Map.put(config, :db, ConfigDB.get_db_keys(config))
897 end)
898 |> Enum.split_with(fn config ->
899 Ecto.get_meta(config, :state) == :deleted
900 end)
901
902 Config.TransferTask.load_and_update_env(deleted, false)
903
904 if !Restarter.Pleroma.need_reboot?() do
905 changed_reboot_settings? =
906 (updated ++ deleted)
907 |> Enum.any?(fn config ->
908 group = ConfigDB.from_string(config.group)
909 key = ConfigDB.from_string(config.key)
910 value = ConfigDB.from_binary(config.value)
911 Config.TransferTask.pleroma_need_restart?(group, key, value)
912 end)
913
914 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
915 end
916
917 conn
918 |> put_view(ConfigView)
919 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
920 end
921 end
922
923 def restart(conn, _params) do
924 with :ok <- configurable_from_database() do
925 Restarter.Pleroma.restart(Config.get(:env), 50)
926
927 json(conn, %{})
928 end
929 end
930
931 def need_reboot(conn, _params) do
932 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
933 end
934
935 defp configurable_from_database do
936 if Config.get(:configurable_from_database) do
937 :ok
938 else
939 {:error, "To use this endpoint you need to enable configuration from database."}
940 end
941 end
942
943 defp whitelisted_config?(group, key) do
944 if whitelisted_configs = Config.get(:database_config_whitelist) do
945 Enum.any?(whitelisted_configs, fn
946 {whitelisted_group} ->
947 group == inspect(whitelisted_group)
948
949 {whitelisted_group, whitelisted_key} ->
950 group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
951 end)
952 else
953 true
954 end
955 end
956
957 defp whitelisted_config?(%{"group" => group, "key" => key}) do
958 whitelisted_config?(group, key)
959 end
960
961 defp whitelisted_config?(%{:group => group} = config) do
962 whitelisted_config?(group, config[:key])
963 end
964
965 def reload_emoji(conn, _params) do
966 Pleroma.Emoji.reload()
967
968 conn |> json("ok")
969 end
970
971 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
972 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
973
974 User.toggle_confirmation(users)
975
976 ModerationLog.insert_log(%{
977 actor: admin,
978 subject: users,
979 action: "confirm_email"
980 })
981
982 conn |> json("")
983 end
984
985 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
986 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
987
988 User.try_send_confirmation_email(users)
989
990 ModerationLog.insert_log(%{
991 actor: admin,
992 subject: users,
993 action: "resend_confirmation_email"
994 })
995
996 conn |> json("")
997 end
998
999 def oauth_app_create(conn, params) do
1000 params =
1001 if params["name"] do
1002 Map.put(params, "client_name", params["name"])
1003 else
1004 params
1005 end
1006
1007 result =
1008 case App.create(params) do
1009 {:ok, app} ->
1010 AppView.render("show.json", %{app: app, admin: true})
1011
1012 {:error, changeset} ->
1013 App.errors(changeset)
1014 end
1015
1016 json(conn, result)
1017 end
1018
1019 def oauth_app_update(conn, params) do
1020 params =
1021 if params["name"] do
1022 Map.put(params, "client_name", params["name"])
1023 else
1024 params
1025 end
1026
1027 with {:ok, app} <- App.update(params) do
1028 json(conn, AppView.render("show.json", %{app: app, admin: true}))
1029 else
1030 {:error, changeset} ->
1031 json(conn, App.errors(changeset))
1032
1033 nil ->
1034 json_response(conn, :bad_request, "")
1035 end
1036 end
1037
1038 def oauth_app_list(conn, params) do
1039 {page, page_size} = page_params(params)
1040
1041 search_params = %{
1042 client_name: params["name"],
1043 client_id: params["client_id"],
1044 page: page,
1045 page_size: page_size
1046 }
1047
1048 search_params =
1049 if Map.has_key?(params, "trusted") do
1050 Map.put(search_params, :trusted, params["trusted"])
1051 else
1052 search_params
1053 end
1054
1055 with {:ok, apps, count} <- App.search(search_params) do
1056 json(
1057 conn,
1058 AppView.render("index.json",
1059 apps: apps,
1060 count: count,
1061 page_size: page_size,
1062 admin: true
1063 )
1064 )
1065 end
1066 end
1067
1068 def oauth_app_delete(conn, params) do
1069 with {:ok, _app} <- App.destroy(params["id"]) do
1070 json_response(conn, :no_content, "")
1071 else
1072 _ -> json_response(conn, :bad_request, "")
1073 end
1074 end
1075
1076 def stats(conn, _) do
1077 count = Stats.get_status_visibility_count()
1078
1079 conn
1080 |> json(%{"status_visibility" => count})
1081 end
1082
1083 defp page_params(params) do
1084 {get_page(params["page"]), get_page_size(params["page_size"])}
1085 end
1086
1087 defp get_page(page_string) when is_nil(page_string), do: 1
1088
1089 defp get_page(page_string) do
1090 case Integer.parse(page_string) do
1091 {page, _} -> page
1092 :error -> 1
1093 end
1094 end
1095
1096 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1097
1098 defp get_page_size(page_size_string) do
1099 case Integer.parse(page_size_string) do
1100 {page_size, _} -> page_size
1101 :error -> @users_page_size
1102 end
1103 end
1104 end