1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
6 use Pleroma.Web.ConnCase
7 use Oban.Testing, repo: Pleroma.Repo
9 import ExUnit.CaptureLog
11 import Pleroma.Factory
13 alias Pleroma.Activity
17 alias Pleroma.ModerationLog
19 alias Pleroma.Tests.ObanHelpers
22 alias Pleroma.Web.ActivityPub.Relay
23 alias Pleroma.Web.CommonAPI
24 alias Pleroma.Web.MediaProxy
27 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
33 admin = insert(:user, is_admin: true)
34 token = insert(:oauth_admin_token, user: admin)
38 |> assign(:user, admin)
39 |> assign(:token, token)
41 {:ok, %{admin: admin, token: token, conn: conn}}
44 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
45 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
47 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
50 url = "/api/pleroma/admin/users/#{user.nickname}"
52 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
53 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
54 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
56 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
57 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
60 for good_token <- [good_token1, good_token2, good_token3] do
63 |> assign(:user, admin)
64 |> assign(:token, good_token)
67 assert json_response(conn, 200)
70 for good_token <- [good_token1, good_token2, good_token3] do
74 |> assign(:token, good_token)
77 assert json_response(conn, :forbidden)
80 for bad_token <- [bad_token1, bad_token2, bad_token3] do
83 |> assign(:user, admin)
84 |> assign(:token, bad_token)
87 assert json_response(conn, :forbidden)
92 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
93 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
95 test "GET /api/pleroma/admin/users/:nickname requires " <>
96 "read:accounts or admin:read:accounts or broader scope",
99 url = "/api/pleroma/admin/users/#{user.nickname}"
101 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
102 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
103 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
104 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
105 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
107 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
109 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
110 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
113 for good_token <- good_tokens do
116 |> assign(:user, admin)
117 |> assign(:token, good_token)
120 assert json_response(conn, 200)
123 for good_token <- good_tokens do
126 |> assign(:user, nil)
127 |> assign(:token, good_token)
130 assert json_response(conn, :forbidden)
133 for bad_token <- [bad_token1, bad_token2, bad_token3] do
136 |> assign(:user, admin)
137 |> assign(:token, bad_token)
140 assert json_response(conn, :forbidden)
145 describe "DELETE /api/pleroma/admin/users" do
146 test "single user", %{admin: admin, conn: conn} do
148 clear_config([:instance, :federating], true)
150 with_mock Pleroma.Web.Federator,
151 publish: fn _ -> nil end do
154 |> put_req_header("accept", "application/json")
155 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
157 ObanHelpers.perform_all()
159 assert User.get_by_nickname(user.nickname).deactivated
161 log_entry = Repo.one(ModerationLog)
163 assert ModerationLog.get_log_entry_message(log_entry) ==
164 "@#{admin.nickname} deleted users: @#{user.nickname}"
166 assert json_response(conn, 200) == [user.nickname]
168 assert called(Pleroma.Web.Federator.publish(:_))
172 test "multiple users", %{admin: admin, conn: conn} do
173 user_one = insert(:user)
174 user_two = insert(:user)
178 |> put_req_header("accept", "application/json")
179 |> delete("/api/pleroma/admin/users", %{
180 nicknames: [user_one.nickname, user_two.nickname]
183 log_entry = Repo.one(ModerationLog)
185 assert ModerationLog.get_log_entry_message(log_entry) ==
186 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
188 response = json_response(conn, 200)
189 assert response -- [user_one.nickname, user_two.nickname] == []
193 describe "/api/pleroma/admin/users" do
194 test "Create", %{conn: conn} do
197 |> put_req_header("accept", "application/json")
198 |> post("/api/pleroma/admin/users", %{
201 "nickname" => "lain",
202 "email" => "lain@example.org",
206 "nickname" => "lain2",
207 "email" => "lain2@example.org",
213 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
214 assert response == ["success", "success"]
216 log_entry = Repo.one(ModerationLog)
218 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
221 test "Cannot create user with existing email", %{conn: conn} do
226 |> put_req_header("accept", "application/json")
227 |> post("/api/pleroma/admin/users", %{
230 "nickname" => "lain",
231 "email" => user.email,
237 assert json_response(conn, 409) == [
241 "email" => user.email,
244 "error" => "email has already been taken",
250 test "Cannot create user with existing nickname", %{conn: conn} do
255 |> put_req_header("accept", "application/json")
256 |> post("/api/pleroma/admin/users", %{
259 "nickname" => user.nickname,
260 "email" => "someuser@plerama.social",
266 assert json_response(conn, 409) == [
270 "email" => "someuser@plerama.social",
271 "nickname" => user.nickname
273 "error" => "nickname has already been taken",
279 test "Multiple user creation works in transaction", %{conn: conn} do
284 |> put_req_header("accept", "application/json")
285 |> post("/api/pleroma/admin/users", %{
288 "nickname" => "newuser",
289 "email" => "newuser@pleroma.social",
293 "nickname" => "lain",
294 "email" => user.email,
300 assert json_response(conn, 409) == [
304 "email" => user.email,
307 "error" => "email has already been taken",
313 "email" => "newuser@pleroma.social",
314 "nickname" => "newuser"
321 assert User.get_by_nickname("newuser") === nil
325 describe "/api/pleroma/admin/users/:nickname" do
326 test "Show", %{conn: conn} do
329 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
332 "deactivated" => false,
333 "id" => to_string(user.id),
335 "nickname" => user.nickname,
336 "roles" => %{"admin" => false, "moderator" => false},
338 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
339 "display_name" => HTML.strip_tags(user.name || user.nickname),
340 "confirmation_pending" => false,
341 "approval_pending" => false,
343 "registration_reason" => nil
346 assert expected == json_response(conn, 200)
349 test "when the user doesn't exist", %{conn: conn} do
352 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
354 assert %{"error" => "Not found"} == json_response(conn, 404)
358 describe "/api/pleroma/admin/users/follow" do
359 test "allows to force-follow another user", %{admin: admin, conn: conn} do
361 follower = insert(:user)
364 |> put_req_header("accept", "application/json")
365 |> post("/api/pleroma/admin/users/follow", %{
366 "follower" => follower.nickname,
367 "followed" => user.nickname
370 user = User.get_cached_by_id(user.id)
371 follower = User.get_cached_by_id(follower.id)
373 assert User.following?(follower, user)
375 log_entry = Repo.one(ModerationLog)
377 assert ModerationLog.get_log_entry_message(log_entry) ==
378 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
382 describe "/api/pleroma/admin/users/unfollow" do
383 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
385 follower = insert(:user)
387 User.follow(follower, user)
390 |> put_req_header("accept", "application/json")
391 |> post("/api/pleroma/admin/users/unfollow", %{
392 "follower" => follower.nickname,
393 "followed" => user.nickname
396 user = User.get_cached_by_id(user.id)
397 follower = User.get_cached_by_id(follower.id)
399 refute User.following?(follower, user)
401 log_entry = Repo.one(ModerationLog)
403 assert ModerationLog.get_log_entry_message(log_entry) ==
404 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
408 describe "PUT /api/pleroma/admin/users/tag" do
409 setup %{conn: conn} do
410 user1 = insert(:user, %{tags: ["x"]})
411 user2 = insert(:user, %{tags: ["y"]})
412 user3 = insert(:user, %{tags: ["unchanged"]})
416 |> put_req_header("accept", "application/json")
418 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
419 "#{user2.nickname}&tags[]=foo&tags[]=bar"
422 %{conn: conn, user1: user1, user2: user2, user3: user3}
425 test "it appends specified tags to users with specified nicknames", %{
431 assert json_response(conn, :no_content)
432 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
433 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
435 log_entry = Repo.one(ModerationLog)
438 [user1.nickname, user2.nickname]
439 |> Enum.map(&"@#{&1}")
442 tags = ["foo", "bar"] |> Enum.join(", ")
444 assert ModerationLog.get_log_entry_message(log_entry) ==
445 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
448 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
449 assert json_response(conn, :no_content)
450 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
454 describe "DELETE /api/pleroma/admin/users/tag" do
455 setup %{conn: conn} do
456 user1 = insert(:user, %{tags: ["x"]})
457 user2 = insert(:user, %{tags: ["y", "z"]})
458 user3 = insert(:user, %{tags: ["unchanged"]})
462 |> put_req_header("accept", "application/json")
464 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
465 "#{user2.nickname}&tags[]=x&tags[]=z"
468 %{conn: conn, user1: user1, user2: user2, user3: user3}
471 test "it removes specified tags from users with specified nicknames", %{
477 assert json_response(conn, :no_content)
478 assert User.get_cached_by_id(user1.id).tags == []
479 assert User.get_cached_by_id(user2.id).tags == ["y"]
481 log_entry = Repo.one(ModerationLog)
484 [user1.nickname, user2.nickname]
485 |> Enum.map(&"@#{&1}")
488 tags = ["x", "z"] |> Enum.join(", ")
490 assert ModerationLog.get_log_entry_message(log_entry) ==
491 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
494 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
495 assert json_response(conn, :no_content)
496 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
500 describe "/api/pleroma/admin/users/:nickname/permission_group" do
501 test "GET is giving user_info", %{admin: admin, conn: conn} do
504 |> put_req_header("accept", "application/json")
505 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
507 assert json_response(conn, 200) == %{
509 "is_moderator" => false
513 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
518 |> put_req_header("accept", "application/json")
519 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
521 assert json_response(conn, 200) == %{
525 log_entry = Repo.one(ModerationLog)
527 assert ModerationLog.get_log_entry_message(log_entry) ==
528 "@#{admin.nickname} made @#{user.nickname} admin"
531 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
532 user_one = insert(:user)
533 user_two = insert(:user)
537 |> put_req_header("accept", "application/json")
538 |> post("/api/pleroma/admin/users/permission_group/admin", %{
539 nicknames: [user_one.nickname, user_two.nickname]
542 assert json_response(conn, 200) == %{"is_admin" => true}
544 log_entry = Repo.one(ModerationLog)
546 assert ModerationLog.get_log_entry_message(log_entry) ==
547 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
550 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
551 user = insert(:user, is_admin: true)
555 |> put_req_header("accept", "application/json")
556 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
558 assert json_response(conn, 200) == %{"is_admin" => false}
560 log_entry = Repo.one(ModerationLog)
562 assert ModerationLog.get_log_entry_message(log_entry) ==
563 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
566 test "/:right DELETE, can remove from a permission group (multiple)", %{
570 user_one = insert(:user, is_admin: true)
571 user_two = insert(:user, is_admin: true)
575 |> put_req_header("accept", "application/json")
576 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
577 nicknames: [user_one.nickname, user_two.nickname]
580 assert json_response(conn, 200) == %{"is_admin" => false}
582 log_entry = Repo.one(ModerationLog)
584 assert ModerationLog.get_log_entry_message(log_entry) ==
585 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
591 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
596 |> put_req_header("accept", "application/json")
597 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
599 resp = json_response(conn, 200)
601 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
604 describe "GET /api/pleroma/admin/users" do
605 test "renders users array for the first page", %{conn: conn, admin: admin} do
606 user = insert(:user, local: false, tags: ["foo", "bar"])
607 user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude")
609 conn = get(conn, "/api/pleroma/admin/users?page=1")
614 "deactivated" => admin.deactivated,
616 "nickname" => admin.nickname,
617 "roles" => %{"admin" => true, "moderator" => false},
620 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
621 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
622 "confirmation_pending" => false,
623 "approval_pending" => false,
624 "url" => admin.ap_id,
625 "registration_reason" => nil
628 "deactivated" => user.deactivated,
630 "nickname" => user.nickname,
631 "roles" => %{"admin" => false, "moderator" => false},
633 "tags" => ["foo", "bar"],
634 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
635 "display_name" => HTML.strip_tags(user.name || user.nickname),
636 "confirmation_pending" => false,
637 "approval_pending" => false,
639 "registration_reason" => nil
642 "deactivated" => user2.deactivated,
644 "nickname" => user2.nickname,
645 "roles" => %{"admin" => false, "moderator" => false},
648 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
649 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
650 "confirmation_pending" => false,
651 "approval_pending" => true,
652 "url" => user2.ap_id,
653 "registration_reason" => "I'm a chill dude"
656 |> Enum.sort_by(& &1["nickname"])
658 assert json_response(conn, 200) == %{
665 test "pagination works correctly with service users", %{conn: conn} do
666 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
668 insert_list(25, :user)
670 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
672 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
673 |> json_response(200)
675 assert Enum.count(users1) == 10
676 assert service1 not in users1
678 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
680 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
681 |> json_response(200)
683 assert Enum.count(users2) == 10
684 assert service1 not in users2
686 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
688 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
689 |> json_response(200)
691 assert Enum.count(users3) == 6
692 assert service1 not in users3
695 test "renders empty array for the second page", %{conn: conn} do
698 conn = get(conn, "/api/pleroma/admin/users?page=2")
700 assert json_response(conn, 200) == %{
707 test "regular search", %{conn: conn} do
708 user = insert(:user, nickname: "bob")
710 conn = get(conn, "/api/pleroma/admin/users?query=bo")
712 assert json_response(conn, 200) == %{
717 "deactivated" => user.deactivated,
719 "nickname" => user.nickname,
720 "roles" => %{"admin" => false, "moderator" => false},
723 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
724 "display_name" => HTML.strip_tags(user.name || user.nickname),
725 "confirmation_pending" => false,
726 "approval_pending" => false,
728 "registration_reason" => nil
734 test "search by domain", %{conn: conn} do
735 user = insert(:user, nickname: "nickname@domain.com")
738 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
740 assert json_response(conn, 200) == %{
745 "deactivated" => user.deactivated,
747 "nickname" => user.nickname,
748 "roles" => %{"admin" => false, "moderator" => false},
751 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
752 "display_name" => HTML.strip_tags(user.name || user.nickname),
753 "confirmation_pending" => false,
754 "approval_pending" => false,
756 "registration_reason" => nil
762 test "search by full nickname", %{conn: conn} do
763 user = insert(:user, nickname: "nickname@domain.com")
766 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
768 assert json_response(conn, 200) == %{
773 "deactivated" => user.deactivated,
775 "nickname" => user.nickname,
776 "roles" => %{"admin" => false, "moderator" => false},
779 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
780 "display_name" => HTML.strip_tags(user.name || user.nickname),
781 "confirmation_pending" => false,
782 "approval_pending" => false,
784 "registration_reason" => nil
790 test "search by display name", %{conn: conn} do
791 user = insert(:user, name: "Display name")
794 conn = get(conn, "/api/pleroma/admin/users?name=display")
796 assert json_response(conn, 200) == %{
801 "deactivated" => user.deactivated,
803 "nickname" => user.nickname,
804 "roles" => %{"admin" => false, "moderator" => false},
807 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
808 "display_name" => HTML.strip_tags(user.name || user.nickname),
809 "confirmation_pending" => false,
810 "approval_pending" => false,
812 "registration_reason" => nil
818 test "search by email", %{conn: conn} do
819 user = insert(:user, email: "email@example.com")
822 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
824 assert json_response(conn, 200) == %{
829 "deactivated" => user.deactivated,
831 "nickname" => user.nickname,
832 "roles" => %{"admin" => false, "moderator" => false},
835 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
836 "display_name" => HTML.strip_tags(user.name || user.nickname),
837 "confirmation_pending" => false,
838 "approval_pending" => false,
840 "registration_reason" => nil
846 test "regular search with page size", %{conn: conn} do
847 user = insert(:user, nickname: "aalice")
848 user2 = insert(:user, nickname: "alice")
850 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
852 assert json_response(conn1, 200) == %{
857 "deactivated" => user.deactivated,
859 "nickname" => user.nickname,
860 "roles" => %{"admin" => false, "moderator" => false},
863 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
864 "display_name" => HTML.strip_tags(user.name || user.nickname),
865 "confirmation_pending" => false,
866 "approval_pending" => false,
868 "registration_reason" => nil
873 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
875 assert json_response(conn2, 200) == %{
880 "deactivated" => user2.deactivated,
882 "nickname" => user2.nickname,
883 "roles" => %{"admin" => false, "moderator" => false},
886 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
887 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
888 "confirmation_pending" => false,
889 "approval_pending" => false,
890 "url" => user2.ap_id,
891 "registration_reason" => nil
897 test "only local users" do
898 admin = insert(:user, is_admin: true, nickname: "john")
899 token = insert(:oauth_admin_token, user: admin)
900 user = insert(:user, nickname: "bob")
902 insert(:user, nickname: "bobb", local: false)
906 |> assign(:user, admin)
907 |> assign(:token, token)
908 |> get("/api/pleroma/admin/users?query=bo&filters=local")
910 assert json_response(conn, 200) == %{
915 "deactivated" => user.deactivated,
917 "nickname" => user.nickname,
918 "roles" => %{"admin" => false, "moderator" => false},
921 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
922 "display_name" => HTML.strip_tags(user.name || user.nickname),
923 "confirmation_pending" => false,
924 "approval_pending" => false,
926 "registration_reason" => nil
932 test "only local users with no query", %{conn: conn, admin: old_admin} do
933 admin = insert(:user, is_admin: true, nickname: "john")
934 user = insert(:user, nickname: "bob")
936 insert(:user, nickname: "bobb", local: false)
938 conn = get(conn, "/api/pleroma/admin/users?filters=local")
943 "deactivated" => user.deactivated,
945 "nickname" => user.nickname,
946 "roles" => %{"admin" => false, "moderator" => false},
949 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
950 "display_name" => HTML.strip_tags(user.name || user.nickname),
951 "confirmation_pending" => false,
952 "approval_pending" => false,
954 "registration_reason" => nil
957 "deactivated" => admin.deactivated,
959 "nickname" => admin.nickname,
960 "roles" => %{"admin" => true, "moderator" => false},
963 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
964 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
965 "confirmation_pending" => false,
966 "approval_pending" => false,
967 "url" => admin.ap_id,
968 "registration_reason" => nil
971 "deactivated" => false,
972 "id" => old_admin.id,
974 "nickname" => old_admin.nickname,
975 "roles" => %{"admin" => true, "moderator" => false},
977 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
978 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
979 "confirmation_pending" => false,
980 "approval_pending" => false,
981 "url" => old_admin.ap_id,
982 "registration_reason" => nil
985 |> Enum.sort_by(& &1["nickname"])
987 assert json_response(conn, 200) == %{
994 test "load only admins", %{conn: conn, admin: admin} do
995 second_admin = insert(:user, is_admin: true)
999 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
1004 "deactivated" => false,
1006 "nickname" => admin.nickname,
1007 "roles" => %{"admin" => true, "moderator" => false},
1008 "local" => admin.local,
1010 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1011 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1012 "confirmation_pending" => false,
1013 "approval_pending" => false,
1014 "url" => admin.ap_id,
1015 "registration_reason" => nil
1018 "deactivated" => false,
1019 "id" => second_admin.id,
1020 "nickname" => second_admin.nickname,
1021 "roles" => %{"admin" => true, "moderator" => false},
1022 "local" => second_admin.local,
1024 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1025 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1026 "confirmation_pending" => false,
1027 "approval_pending" => false,
1028 "url" => second_admin.ap_id,
1029 "registration_reason" => nil
1032 |> Enum.sort_by(& &1["nickname"])
1034 assert json_response(conn, 200) == %{
1041 test "load only moderators", %{conn: conn} do
1042 moderator = insert(:user, is_moderator: true)
1046 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1048 assert json_response(conn, 200) == %{
1053 "deactivated" => false,
1054 "id" => moderator.id,
1055 "nickname" => moderator.nickname,
1056 "roles" => %{"admin" => false, "moderator" => true},
1057 "local" => moderator.local,
1059 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1060 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1061 "confirmation_pending" => false,
1062 "approval_pending" => false,
1063 "url" => moderator.ap_id,
1064 "registration_reason" => nil
1070 test "load users with tags list", %{conn: conn} do
1071 user1 = insert(:user, tags: ["first"])
1072 user2 = insert(:user, tags: ["second"])
1076 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1081 "deactivated" => false,
1083 "nickname" => user1.nickname,
1084 "roles" => %{"admin" => false, "moderator" => false},
1085 "local" => user1.local,
1086 "tags" => ["first"],
1087 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1088 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1089 "confirmation_pending" => false,
1090 "approval_pending" => false,
1091 "url" => user1.ap_id,
1092 "registration_reason" => nil
1095 "deactivated" => false,
1097 "nickname" => user2.nickname,
1098 "roles" => %{"admin" => false, "moderator" => false},
1099 "local" => user2.local,
1100 "tags" => ["second"],
1101 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1102 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1103 "confirmation_pending" => false,
1104 "approval_pending" => false,
1105 "url" => user2.ap_id,
1106 "registration_reason" => nil
1109 |> Enum.sort_by(& &1["nickname"])
1111 assert json_response(conn, 200) == %{
1118 test "it works with multiple filters" do
1119 admin = insert(:user, nickname: "john", is_admin: true)
1120 token = insert(:oauth_admin_token, user: admin)
1121 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1123 insert(:user, nickname: "ken", local: true, deactivated: true)
1124 insert(:user, nickname: "bobb", local: false, deactivated: false)
1128 |> assign(:user, admin)
1129 |> assign(:token, token)
1130 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1132 assert json_response(conn, 200) == %{
1137 "deactivated" => user.deactivated,
1139 "nickname" => user.nickname,
1140 "roles" => %{"admin" => false, "moderator" => false},
1141 "local" => user.local,
1143 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1144 "display_name" => HTML.strip_tags(user.name || user.nickname),
1145 "confirmation_pending" => false,
1146 "approval_pending" => false,
1147 "url" => user.ap_id,
1148 "registration_reason" => nil
1154 test "it omits relay user", %{admin: admin, conn: conn} do
1155 assert %User{} = Relay.get_actor()
1157 conn = get(conn, "/api/pleroma/admin/users")
1159 assert json_response(conn, 200) == %{
1164 "deactivated" => admin.deactivated,
1166 "nickname" => admin.nickname,
1167 "roles" => %{"admin" => true, "moderator" => false},
1170 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1171 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1172 "confirmation_pending" => false,
1173 "approval_pending" => false,
1174 "url" => admin.ap_id,
1175 "registration_reason" => nil
1182 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1183 user_one = insert(:user, deactivated: true)
1184 user_two = insert(:user, deactivated: true)
1189 "/api/pleroma/admin/users/activate",
1190 %{nicknames: [user_one.nickname, user_two.nickname]}
1193 response = json_response(conn, 200)
1194 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1196 log_entry = Repo.one(ModerationLog)
1198 assert ModerationLog.get_log_entry_message(log_entry) ==
1199 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1202 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1203 user_one = insert(:user, deactivated: false)
1204 user_two = insert(:user, deactivated: false)
1209 "/api/pleroma/admin/users/deactivate",
1210 %{nicknames: [user_one.nickname, user_two.nickname]}
1213 response = json_response(conn, 200)
1214 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1216 log_entry = Repo.one(ModerationLog)
1218 assert ModerationLog.get_log_entry_message(log_entry) ==
1219 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1222 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1223 user = insert(:user)
1225 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1227 assert json_response(conn, 200) ==
1229 "deactivated" => !user.deactivated,
1231 "nickname" => user.nickname,
1232 "roles" => %{"admin" => false, "moderator" => false},
1235 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1236 "display_name" => HTML.strip_tags(user.name || user.nickname),
1237 "confirmation_pending" => false,
1238 "approval_pending" => false,
1239 "url" => user.ap_id,
1240 "registration_reason" => nil
1243 log_entry = Repo.one(ModerationLog)
1245 assert ModerationLog.get_log_entry_message(log_entry) ==
1246 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1249 describe "PUT disable_mfa" do
1250 test "returns 200 and disable 2fa", %{conn: conn} do
1253 multi_factor_authentication_settings: %MFA.Settings{
1255 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1261 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1262 |> json_response(200)
1264 assert response == user.nickname
1265 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1267 refute mfa_settings.enabled
1268 refute mfa_settings.totp.confirmed
1271 test "returns 404 if user not found", %{conn: conn} do
1274 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1275 |> json_response(404)
1277 assert response == %{"error" => "Not found"}
1281 describe "GET /api/pleroma/admin/restart" do
1282 setup do: clear_config(:configurable_from_database, true)
1284 test "pleroma restarts", %{conn: conn} do
1286 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1287 end) =~ "pleroma restarted"
1289 refute Restarter.Pleroma.need_reboot?()
1293 test "need_reboot flag", %{conn: conn} do
1295 |> get("/api/pleroma/admin/need_reboot")
1296 |> json_response(200) == %{"need_reboot" => false}
1298 Restarter.Pleroma.need_reboot()
1301 |> get("/api/pleroma/admin/need_reboot")
1302 |> json_response(200) == %{"need_reboot" => true}
1304 on_exit(fn -> Restarter.Pleroma.refresh() end)
1307 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
1309 user = insert(:user)
1311 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1312 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1313 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1315 insert(:note_activity, user: user, published: date1)
1316 insert(:note_activity, user: user, published: date2)
1317 insert(:note_activity, user: user, published: date3)
1322 test "renders user's statuses", %{conn: conn, user: user} do
1323 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1325 assert json_response(conn, 200) |> length() == 3
1328 test "renders user's statuses with a limit", %{conn: conn, user: user} do
1329 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
1331 assert json_response(conn, 200) |> length() == 2
1334 test "doesn't return private statuses by default", %{conn: conn, user: user} do
1335 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1337 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1339 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1341 assert json_response(conn, 200) |> length() == 4
1344 test "returns private statuses with godmode on", %{conn: conn, user: user} do
1345 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1347 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1349 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
1351 assert json_response(conn, 200) |> length() == 5
1354 test "excludes reblogs by default", %{conn: conn, user: user} do
1355 other_user = insert(:user)
1356 {:ok, activity} = CommonAPI.post(user, %{status: "."})
1357 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
1359 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
1360 assert json_response(conn_res, 200) |> length() == 0
1363 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
1365 assert json_response(conn_res, 200) |> length() == 1
1369 describe "GET /api/pleroma/admin/moderation_log" do
1371 moderator = insert(:user, is_moderator: true)
1373 %{moderator: moderator}
1376 test "returns the log", %{conn: conn, admin: admin} do
1377 Repo.insert(%ModerationLog{
1381 "nickname" => admin.nickname,
1384 action: "relay_follow",
1385 target: "https://example.org/relay"
1387 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1390 Repo.insert(%ModerationLog{
1394 "nickname" => admin.nickname,
1397 action: "relay_unfollow",
1398 target: "https://example.org/relay"
1400 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1403 conn = get(conn, "/api/pleroma/admin/moderation_log")
1405 response = json_response(conn, 200)
1406 [first_entry, second_entry] = response["items"]
1408 assert response["total"] == 2
1409 assert first_entry["data"]["action"] == "relay_unfollow"
1411 assert first_entry["message"] ==
1412 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1414 assert second_entry["data"]["action"] == "relay_follow"
1416 assert second_entry["message"] ==
1417 "@#{admin.nickname} followed relay: https://example.org/relay"
1420 test "returns the log with pagination", %{conn: conn, admin: admin} do
1421 Repo.insert(%ModerationLog{
1425 "nickname" => admin.nickname,
1428 action: "relay_follow",
1429 target: "https://example.org/relay"
1431 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1434 Repo.insert(%ModerationLog{
1438 "nickname" => admin.nickname,
1441 action: "relay_unfollow",
1442 target: "https://example.org/relay"
1444 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1447 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
1449 response1 = json_response(conn1, 200)
1450 [first_entry] = response1["items"]
1452 assert response1["total"] == 2
1453 assert response1["items"] |> length() == 1
1454 assert first_entry["data"]["action"] == "relay_unfollow"
1456 assert first_entry["message"] ==
1457 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1459 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
1461 response2 = json_response(conn2, 200)
1462 [second_entry] = response2["items"]
1464 assert response2["total"] == 2
1465 assert response2["items"] |> length() == 1
1466 assert second_entry["data"]["action"] == "relay_follow"
1468 assert second_entry["message"] ==
1469 "@#{admin.nickname} followed relay: https://example.org/relay"
1472 test "filters log by date", %{conn: conn, admin: admin} do
1473 first_date = "2017-08-15T15:47:06Z"
1474 second_date = "2017-08-20T15:47:06Z"
1476 Repo.insert(%ModerationLog{
1480 "nickname" => admin.nickname,
1483 action: "relay_follow",
1484 target: "https://example.org/relay"
1486 inserted_at: NaiveDateTime.from_iso8601!(first_date)
1489 Repo.insert(%ModerationLog{
1493 "nickname" => admin.nickname,
1496 action: "relay_unfollow",
1497 target: "https://example.org/relay"
1499 inserted_at: NaiveDateTime.from_iso8601!(second_date)
1505 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
1508 response1 = json_response(conn1, 200)
1509 [first_entry] = response1["items"]
1511 assert response1["total"] == 1
1512 assert first_entry["data"]["action"] == "relay_unfollow"
1514 assert first_entry["message"] ==
1515 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1518 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
1519 Repo.insert(%ModerationLog{
1523 "nickname" => admin.nickname,
1526 action: "relay_follow",
1527 target: "https://example.org/relay"
1531 Repo.insert(%ModerationLog{
1534 "id" => moderator.id,
1535 "nickname" => moderator.nickname,
1538 action: "relay_unfollow",
1539 target: "https://example.org/relay"
1543 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
1545 response1 = json_response(conn1, 200)
1546 [first_entry] = response1["items"]
1548 assert response1["total"] == 1
1549 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
1552 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
1553 ModerationLog.insert_log(%{
1555 action: "relay_follow",
1556 target: "https://example.org/relay"
1559 ModerationLog.insert_log(%{
1561 action: "relay_unfollow",
1562 target: "https://example.org/relay"
1565 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
1567 response1 = json_response(conn1, 200)
1568 [first_entry] = response1["items"]
1570 assert response1["total"] == 1
1572 assert get_in(first_entry, ["data", "message"]) ==
1573 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
1577 test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated",
1579 clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated)
1580 user = insert(:user, %{local: false, nickname: "u@peer1.com"})
1581 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1583 assert json_response(conn, 200)
1586 describe "GET /users/:nickname/credentials" do
1587 test "gets the user credentials", %{conn: conn} do
1588 user = insert(:user)
1589 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1591 response = assert json_response(conn, 200)
1592 assert response["email"] == user.email
1595 test "returns 403 if requested by a non-admin" do
1596 user = insert(:user)
1600 |> assign(:user, user)
1601 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
1603 assert json_response(conn, :forbidden)
1607 describe "PATCH /users/:nickname/credentials" do
1609 user = insert(:user)
1613 test "changes password and email", %{conn: conn, admin: admin, user: user} do
1614 assert user.password_reset_pending == false
1617 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1618 "password" => "new_password",
1619 "email" => "new_email@example.com",
1620 "name" => "new_name"
1623 assert json_response(conn, 200) == %{"status" => "success"}
1625 ObanHelpers.perform_all()
1627 updated_user = User.get_by_id(user.id)
1629 assert updated_user.email == "new_email@example.com"
1630 assert updated_user.name == "new_name"
1631 assert updated_user.password_hash != user.password_hash
1632 assert updated_user.password_reset_pending == true
1634 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
1636 assert ModerationLog.get_log_entry_message(log_entry1) ==
1637 "@#{admin.nickname} updated users: @#{user.nickname}"
1639 assert ModerationLog.get_log_entry_message(log_entry2) ==
1640 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
1643 test "returns 403 if requested by a non-admin", %{user: user} do
1646 |> assign(:user, user)
1647 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1648 "password" => "new_password",
1649 "email" => "new_email@example.com",
1650 "name" => "new_name"
1653 assert json_response(conn, :forbidden)
1656 test "changes actor type from permitted list", %{conn: conn, user: user} do
1657 assert user.actor_type == "Person"
1659 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1660 "actor_type" => "Service"
1662 |> json_response(200) == %{"status" => "success"}
1664 updated_user = User.get_by_id(user.id)
1666 assert updated_user.actor_type == "Service"
1668 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1669 "actor_type" => "Application"
1671 |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}
1674 test "update non existing user", %{conn: conn} do
1675 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
1676 "password" => "new_password"
1678 |> json_response(404) == %{"error" => "Not found"}
1682 describe "PATCH /users/:nickname/force_password_reset" do
1683 test "sets password_reset_pending to true", %{conn: conn} do
1684 user = insert(:user)
1685 assert user.password_reset_pending == false
1688 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
1690 assert json_response(conn, 204) == ""
1692 ObanHelpers.perform_all()
1694 assert User.get_by_id(user.id).password_reset_pending == true
1698 describe "instances" do
1699 test "GET /instances/:instance/statuses", %{conn: conn} do
1700 user = insert(:user, local: false, nickname: "archaeme@archae.me")
1701 user2 = insert(:user, local: false, nickname: "test@test.com")
1702 insert_pair(:note_activity, user: user)
1703 activity = insert(:note_activity, user: user2)
1705 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1707 response = json_response(ret_conn, 200)
1709 assert length(response) == 2
1711 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
1713 response = json_response(ret_conn, 200)
1715 assert length(response) == 1
1717 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
1719 response = json_response(ret_conn, 200)
1721 assert Enum.empty?(response)
1723 CommonAPI.repeat(activity.id, user)
1725 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1726 response = json_response(ret_conn, 200)
1727 assert length(response) == 2
1729 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
1730 response = json_response(ret_conn, 200)
1731 assert length(response) == 3
1735 describe "PATCH /confirm_email" do
1736 test "it confirms emails of two users", %{conn: conn, admin: admin} do
1737 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1739 assert first_user.confirmation_pending == true
1740 assert second_user.confirmation_pending == true
1743 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
1745 first_user.nickname,
1746 second_user.nickname
1750 assert ret_conn.status == 200
1752 assert first_user.confirmation_pending == true
1753 assert second_user.confirmation_pending == true
1755 log_entry = Repo.one(ModerationLog)
1757 assert ModerationLog.get_log_entry_message(log_entry) ==
1758 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
1759 second_user.nickname
1764 describe "PATCH /resend_confirmation_email" do
1765 test "it resend emails for two users", %{conn: conn, admin: admin} do
1766 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1769 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
1771 first_user.nickname,
1772 second_user.nickname
1776 assert ret_conn.status == 200
1778 log_entry = Repo.one(ModerationLog)
1780 assert ModerationLog.get_log_entry_message(log_entry) ==
1781 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
1782 second_user.nickname
1787 describe "/api/pleroma/admin/stats" do
1788 test "status visibility count", %{conn: conn} do
1789 admin = insert(:user, is_admin: true)
1790 user = insert(:user)
1791 CommonAPI.post(user, %{visibility: "public", status: "hey"})
1792 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1793 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1797 |> assign(:user, admin)
1798 |> get("/api/pleroma/admin/stats")
1799 |> json_response(200)
1801 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
1802 response["status_visibility"]
1805 test "by instance", %{conn: conn} do
1806 admin = insert(:user, is_admin: true)
1807 user1 = insert(:user)
1808 instance2 = "instance2.tld"
1809 user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
1811 CommonAPI.post(user1, %{visibility: "public", status: "hey"})
1812 CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"})
1813 CommonAPI.post(user2, %{visibility: "private", status: "hey"})
1817 |> assign(:user, admin)
1818 |> get("/api/pleroma/admin/stats", instance: instance2)
1819 |> json_response(200)
1821 assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
1822 response["status_visibility"]
1827 # Needed for testing
1828 defmodule Pleroma.Web.Endpoint.NotReal do
1831 defmodule Pleroma.Captcha.NotReal do