1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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
11 alias Pleroma.ModerationLog
13 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.UserInviteToken
16 alias Pleroma.Web.ActivityPub.Relay
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.StatusView
19 alias Pleroma.Web.MediaProxy
20 import Pleroma.Factory
23 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
28 describe "DELETE /api/pleroma/admin/users" do
30 admin = insert(:user, is_admin: true)
35 |> assign(:user, admin)
36 |> put_req_header("accept", "application/json")
37 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
39 log_entry = Repo.one(ModerationLog)
41 assert ModerationLog.get_log_entry_message(log_entry) ==
42 "@#{admin.nickname} deleted users: @#{user.nickname}"
44 assert json_response(conn, 200) == user.nickname
47 test "multiple users" do
48 admin = insert(:user, is_admin: true)
49 user_one = insert(:user)
50 user_two = insert(:user)
54 |> assign(:user, admin)
55 |> put_req_header("accept", "application/json")
56 |> delete("/api/pleroma/admin/users", %{
57 nicknames: [user_one.nickname, user_two.nickname]
60 log_entry = Repo.one(ModerationLog)
62 assert ModerationLog.get_log_entry_message(log_entry) ==
63 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
65 response = json_response(conn, 200)
66 assert response -- [user_one.nickname, user_two.nickname] == []
70 describe "/api/pleroma/admin/users" do
72 admin = insert(:user, is_admin: true)
76 |> assign(:user, admin)
77 |> put_req_header("accept", "application/json")
78 |> post("/api/pleroma/admin/users", %{
82 "email" => "lain@example.org",
86 "nickname" => "lain2",
87 "email" => "lain2@example.org",
93 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
94 assert response == ["success", "success"]
96 log_entry = Repo.one(ModerationLog)
98 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
101 test "Cannot create user with exisiting email" do
102 admin = insert(:user, is_admin: true)
107 |> assign(:user, admin)
108 |> put_req_header("accept", "application/json")
109 |> post("/api/pleroma/admin/users", %{
112 "nickname" => "lain",
113 "email" => user.email,
119 assert json_response(conn, 409) == [
123 "email" => user.email,
126 "error" => "email has already been taken",
132 test "Cannot create user with exisiting nickname" do
133 admin = insert(:user, is_admin: true)
138 |> assign(:user, admin)
139 |> put_req_header("accept", "application/json")
140 |> post("/api/pleroma/admin/users", %{
143 "nickname" => user.nickname,
144 "email" => "someuser@plerama.social",
150 assert json_response(conn, 409) == [
154 "email" => "someuser@plerama.social",
155 "nickname" => user.nickname
157 "error" => "nickname has already been taken",
163 test "Multiple user creation works in transaction" do
164 admin = insert(:user, is_admin: true)
169 |> assign(:user, admin)
170 |> put_req_header("accept", "application/json")
171 |> post("/api/pleroma/admin/users", %{
174 "nickname" => "newuser",
175 "email" => "newuser@pleroma.social",
179 "nickname" => "lain",
180 "email" => user.email,
186 assert json_response(conn, 409) == [
190 "email" => user.email,
193 "error" => "email has already been taken",
199 "email" => "newuser@pleroma.social",
200 "nickname" => "newuser"
207 assert User.get_by_nickname("newuser") === nil
211 describe "/api/pleroma/admin/users/:nickname" do
212 test "Show", %{conn: conn} do
213 admin = insert(:user, is_admin: true)
218 |> assign(:user, admin)
219 |> get("/api/pleroma/admin/users/#{user.nickname}")
222 "deactivated" => false,
223 "id" => to_string(user.id),
225 "nickname" => user.nickname,
226 "roles" => %{"admin" => false, "moderator" => false},
228 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
229 "display_name" => HTML.strip_tags(user.name || user.nickname),
230 "confirmation_pending" => false
233 assert expected == json_response(conn, 200)
236 test "when the user doesn't exist", %{conn: conn} do
237 admin = insert(:user, is_admin: true)
242 |> assign(:user, admin)
243 |> get("/api/pleroma/admin/users/#{user.nickname}")
245 assert "Not found" == json_response(conn, 404)
249 describe "/api/pleroma/admin/users/follow" do
250 test "allows to force-follow another user" do
251 admin = insert(:user, is_admin: true)
253 follower = insert(:user)
256 |> assign(:user, admin)
257 |> put_req_header("accept", "application/json")
258 |> post("/api/pleroma/admin/users/follow", %{
259 "follower" => follower.nickname,
260 "followed" => user.nickname
263 user = User.get_cached_by_id(user.id)
264 follower = User.get_cached_by_id(follower.id)
266 assert User.following?(follower, user)
268 log_entry = Repo.one(ModerationLog)
270 assert ModerationLog.get_log_entry_message(log_entry) ==
271 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
275 describe "/api/pleroma/admin/users/unfollow" do
276 test "allows to force-unfollow another user" do
277 admin = insert(:user, is_admin: true)
279 follower = insert(:user)
281 User.follow(follower, user)
284 |> assign(:user, admin)
285 |> put_req_header("accept", "application/json")
286 |> post("/api/pleroma/admin/users/unfollow", %{
287 "follower" => follower.nickname,
288 "followed" => user.nickname
291 user = User.get_cached_by_id(user.id)
292 follower = User.get_cached_by_id(follower.id)
294 refute User.following?(follower, user)
296 log_entry = Repo.one(ModerationLog)
298 assert ModerationLog.get_log_entry_message(log_entry) ==
299 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
303 describe "PUT /api/pleroma/admin/users/tag" do
305 admin = insert(:user, is_admin: true)
306 user1 = insert(:user, %{tags: ["x"]})
307 user2 = insert(:user, %{tags: ["y"]})
308 user3 = insert(:user, %{tags: ["unchanged"]})
312 |> assign(:user, admin)
313 |> put_req_header("accept", "application/json")
315 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{
317 }&tags[]=foo&tags[]=bar"
320 %{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
323 test "it appends specified tags to users with specified nicknames", %{
329 assert json_response(conn, :no_content)
330 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
331 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
333 log_entry = Repo.one(ModerationLog)
336 [user1.nickname, user2.nickname]
337 |> Enum.map(&"@#{&1}")
340 tags = ["foo", "bar"] |> Enum.join(", ")
342 assert ModerationLog.get_log_entry_message(log_entry) ==
343 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
346 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
347 assert json_response(conn, :no_content)
348 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
352 describe "DELETE /api/pleroma/admin/users/tag" do
354 admin = insert(:user, is_admin: true)
355 user1 = insert(:user, %{tags: ["x"]})
356 user2 = insert(:user, %{tags: ["y", "z"]})
357 user3 = insert(:user, %{tags: ["unchanged"]})
361 |> assign(:user, admin)
362 |> put_req_header("accept", "application/json")
364 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{
369 %{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
372 test "it removes specified tags from users with specified nicknames", %{
378 assert json_response(conn, :no_content)
379 assert User.get_cached_by_id(user1.id).tags == []
380 assert User.get_cached_by_id(user2.id).tags == ["y"]
382 log_entry = Repo.one(ModerationLog)
385 [user1.nickname, user2.nickname]
386 |> Enum.map(&"@#{&1}")
389 tags = ["x", "z"] |> Enum.join(", ")
391 assert ModerationLog.get_log_entry_message(log_entry) ==
392 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
395 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
396 assert json_response(conn, :no_content)
397 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
401 describe "/api/pleroma/admin/users/:nickname/permission_group" do
402 test "GET is giving user_info" do
403 admin = insert(:user, is_admin: true)
407 |> assign(:user, admin)
408 |> put_req_header("accept", "application/json")
409 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
411 assert json_response(conn, 200) == %{
413 "is_moderator" => false
417 test "/:right POST, can add to a permission group" do
418 admin = insert(:user, is_admin: true)
423 |> assign(:user, admin)
424 |> put_req_header("accept", "application/json")
425 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
427 assert json_response(conn, 200) == %{
431 log_entry = Repo.one(ModerationLog)
433 assert ModerationLog.get_log_entry_message(log_entry) ==
434 "@#{admin.nickname} made @#{user.nickname} admin"
437 test "/:right POST, can add to a permission group (multiple)" do
438 admin = insert(:user, is_admin: true)
439 user_one = insert(:user)
440 user_two = insert(:user)
444 |> assign(:user, admin)
445 |> put_req_header("accept", "application/json")
446 |> post("/api/pleroma/admin/users/permission_group/admin", %{
447 nicknames: [user_one.nickname, user_two.nickname]
450 assert json_response(conn, 200) == %{
454 log_entry = Repo.one(ModerationLog)
456 assert ModerationLog.get_log_entry_message(log_entry) ==
457 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
460 test "/:right DELETE, can remove from a permission group" do
461 admin = insert(:user, is_admin: true)
462 user = insert(:user, is_admin: true)
466 |> assign(:user, admin)
467 |> put_req_header("accept", "application/json")
468 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
470 assert json_response(conn, 200) == %{
474 log_entry = Repo.one(ModerationLog)
476 assert ModerationLog.get_log_entry_message(log_entry) ==
477 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
480 test "/:right DELETE, can remove from a permission group (multiple)" do
481 admin = insert(:user, is_admin: true)
482 user_one = insert(:user, is_admin: true)
483 user_two = insert(:user, is_admin: true)
487 |> assign(:user, admin)
488 |> put_req_header("accept", "application/json")
489 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
490 nicknames: [user_one.nickname, user_two.nickname]
493 assert json_response(conn, 200) == %{
497 log_entry = Repo.one(ModerationLog)
499 assert ModerationLog.get_log_entry_message(log_entry) ==
500 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
506 describe "POST /api/pleroma/admin/email_invite, with valid config" do
508 [user: insert(:user, is_admin: true)]
511 clear_config([:instance, :registrations_open]) do
512 Pleroma.Config.put([:instance, :registrations_open], false)
515 clear_config([:instance, :invites_enabled]) do
516 Pleroma.Config.put([:instance, :invites_enabled], true)
519 test "sends invitation and returns 204", %{conn: conn, user: user} do
520 recipient_email = "foo@bar.com"
521 recipient_name = "J. D."
525 |> assign(:user, user)
527 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
530 assert json_response(conn, :no_content)
532 token_record = List.last(Pleroma.Repo.all(Pleroma.UserInviteToken))
534 refute token_record.used
536 notify_email = Pleroma.Config.get([:instance, :notify_email])
537 instance_name = Pleroma.Config.get([:instance, :name])
540 Pleroma.Emails.UserEmail.user_invitation_email(
547 Swoosh.TestAssertions.assert_email_sent(
548 from: {instance_name, notify_email},
549 to: {recipient_name, recipient_email},
550 html_body: email.html_body
554 test "it returns 403 if requested by a non-admin", %{conn: conn} do
555 non_admin_user = insert(:user)
559 |> assign(:user, non_admin_user)
560 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
562 assert json_response(conn, :forbidden)
566 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
568 [user: insert(:user, is_admin: true)]
571 clear_config([:instance, :registrations_open])
572 clear_config([:instance, :invites_enabled])
574 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn, user: user} do
575 Pleroma.Config.put([:instance, :registrations_open], false)
576 Pleroma.Config.put([:instance, :invites_enabled], false)
580 |> assign(:user, user)
581 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
583 assert json_response(conn, :internal_server_error)
586 test "it returns 500 if `registrations_open` is enabled", %{conn: conn, user: user} do
587 Pleroma.Config.put([:instance, :registrations_open], true)
588 Pleroma.Config.put([:instance, :invites_enabled], true)
592 |> assign(:user, user)
593 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
595 assert json_response(conn, :internal_server_error)
599 test "/api/pleroma/admin/users/:nickname/password_reset" do
600 admin = insert(:user, is_admin: true)
605 |> assign(:user, admin)
606 |> put_req_header("accept", "application/json")
607 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
609 resp = json_response(conn, 200)
611 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
614 describe "GET /api/pleroma/admin/users" do
616 admin = insert(:user, is_admin: true)
620 |> assign(:user, admin)
622 {:ok, conn: conn, admin: admin}
625 test "renders users array for the first page", %{conn: conn, admin: admin} do
626 user = insert(:user, local: false, tags: ["foo", "bar"])
627 conn = get(conn, "/api/pleroma/admin/users?page=1")
632 "deactivated" => admin.deactivated,
634 "nickname" => admin.nickname,
635 "roles" => %{"admin" => true, "moderator" => false},
638 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
639 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
640 "confirmation_pending" => false
643 "deactivated" => user.deactivated,
645 "nickname" => user.nickname,
646 "roles" => %{"admin" => false, "moderator" => false},
648 "tags" => ["foo", "bar"],
649 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
650 "display_name" => HTML.strip_tags(user.name || user.nickname),
651 "confirmation_pending" => false
654 |> Enum.sort_by(& &1["nickname"])
656 assert json_response(conn, 200) == %{
663 test "renders empty array for the second page", %{conn: conn} do
666 conn = get(conn, "/api/pleroma/admin/users?page=2")
668 assert json_response(conn, 200) == %{
675 test "regular search", %{conn: conn} do
676 user = insert(:user, nickname: "bob")
678 conn = get(conn, "/api/pleroma/admin/users?query=bo")
680 assert json_response(conn, 200) == %{
685 "deactivated" => user.deactivated,
687 "nickname" => user.nickname,
688 "roles" => %{"admin" => false, "moderator" => false},
691 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
692 "display_name" => HTML.strip_tags(user.name || user.nickname),
693 "confirmation_pending" => false
699 test "search by domain", %{conn: conn} do
700 user = insert(:user, nickname: "nickname@domain.com")
703 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
705 assert json_response(conn, 200) == %{
710 "deactivated" => user.deactivated,
712 "nickname" => user.nickname,
713 "roles" => %{"admin" => false, "moderator" => false},
716 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
717 "display_name" => HTML.strip_tags(user.name || user.nickname),
718 "confirmation_pending" => false
724 test "search by full nickname", %{conn: conn} do
725 user = insert(:user, nickname: "nickname@domain.com")
728 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
730 assert json_response(conn, 200) == %{
735 "deactivated" => user.deactivated,
737 "nickname" => user.nickname,
738 "roles" => %{"admin" => false, "moderator" => false},
741 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
742 "display_name" => HTML.strip_tags(user.name || user.nickname),
743 "confirmation_pending" => false
749 test "search by display name", %{conn: conn} do
750 user = insert(:user, name: "Display name")
753 conn = get(conn, "/api/pleroma/admin/users?name=display")
755 assert json_response(conn, 200) == %{
760 "deactivated" => user.deactivated,
762 "nickname" => user.nickname,
763 "roles" => %{"admin" => false, "moderator" => false},
766 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
767 "display_name" => HTML.strip_tags(user.name || user.nickname),
768 "confirmation_pending" => false
774 test "search by email", %{conn: conn} do
775 user = insert(:user, email: "email@example.com")
778 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
780 assert json_response(conn, 200) == %{
785 "deactivated" => user.deactivated,
787 "nickname" => user.nickname,
788 "roles" => %{"admin" => false, "moderator" => false},
791 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
792 "display_name" => HTML.strip_tags(user.name || user.nickname),
793 "confirmation_pending" => false
799 test "regular search with page size", %{conn: conn} do
800 user = insert(:user, nickname: "aalice")
801 user2 = insert(:user, nickname: "alice")
803 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
805 assert json_response(conn1, 200) == %{
810 "deactivated" => user.deactivated,
812 "nickname" => user.nickname,
813 "roles" => %{"admin" => false, "moderator" => false},
816 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
817 "display_name" => HTML.strip_tags(user.name || user.nickname),
818 "confirmation_pending" => false
823 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
825 assert json_response(conn2, 200) == %{
830 "deactivated" => user2.deactivated,
832 "nickname" => user2.nickname,
833 "roles" => %{"admin" => false, "moderator" => false},
836 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
837 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
838 "confirmation_pending" => false
844 test "only local users" do
845 admin = insert(:user, is_admin: true, nickname: "john")
846 user = insert(:user, nickname: "bob")
848 insert(:user, nickname: "bobb", local: false)
852 |> assign(:user, admin)
853 |> get("/api/pleroma/admin/users?query=bo&filters=local")
855 assert json_response(conn, 200) == %{
860 "deactivated" => user.deactivated,
862 "nickname" => user.nickname,
863 "roles" => %{"admin" => false, "moderator" => false},
866 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
867 "display_name" => HTML.strip_tags(user.name || user.nickname),
868 "confirmation_pending" => false
874 test "only local users with no query", %{admin: old_admin} do
875 admin = insert(:user, is_admin: true, nickname: "john")
876 user = insert(:user, nickname: "bob")
878 insert(:user, nickname: "bobb", local: false)
882 |> assign(:user, admin)
883 |> get("/api/pleroma/admin/users?filters=local")
888 "deactivated" => user.deactivated,
890 "nickname" => user.nickname,
891 "roles" => %{"admin" => false, "moderator" => false},
894 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
895 "display_name" => HTML.strip_tags(user.name || user.nickname),
896 "confirmation_pending" => false
899 "deactivated" => admin.deactivated,
901 "nickname" => admin.nickname,
902 "roles" => %{"admin" => true, "moderator" => false},
905 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
906 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
907 "confirmation_pending" => false
910 "deactivated" => false,
911 "id" => old_admin.id,
913 "nickname" => old_admin.nickname,
914 "roles" => %{"admin" => true, "moderator" => false},
916 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
917 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
918 "confirmation_pending" => false
921 |> Enum.sort_by(& &1["nickname"])
923 assert json_response(conn, 200) == %{
930 test "load only admins", %{conn: conn, admin: admin} do
931 second_admin = insert(:user, is_admin: true)
935 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
940 "deactivated" => false,
942 "nickname" => admin.nickname,
943 "roles" => %{"admin" => true, "moderator" => false},
944 "local" => admin.local,
946 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
947 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
948 "confirmation_pending" => false
951 "deactivated" => false,
952 "id" => second_admin.id,
953 "nickname" => second_admin.nickname,
954 "roles" => %{"admin" => true, "moderator" => false},
955 "local" => second_admin.local,
957 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
958 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
959 "confirmation_pending" => false
962 |> Enum.sort_by(& &1["nickname"])
964 assert json_response(conn, 200) == %{
971 test "load only moderators", %{conn: conn} do
972 moderator = insert(:user, is_moderator: true)
976 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
978 assert json_response(conn, 200) == %{
983 "deactivated" => false,
984 "id" => moderator.id,
985 "nickname" => moderator.nickname,
986 "roles" => %{"admin" => false, "moderator" => true},
987 "local" => moderator.local,
989 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
990 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
991 "confirmation_pending" => false
997 test "load users with tags list", %{conn: conn} do
998 user1 = insert(:user, tags: ["first"])
999 user2 = insert(:user, tags: ["second"])
1003 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1008 "deactivated" => false,
1010 "nickname" => user1.nickname,
1011 "roles" => %{"admin" => false, "moderator" => false},
1012 "local" => user1.local,
1013 "tags" => ["first"],
1014 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1015 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1016 "confirmation_pending" => false
1019 "deactivated" => false,
1021 "nickname" => user2.nickname,
1022 "roles" => %{"admin" => false, "moderator" => false},
1023 "local" => user2.local,
1024 "tags" => ["second"],
1025 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1026 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1027 "confirmation_pending" => false
1030 |> Enum.sort_by(& &1["nickname"])
1032 assert json_response(conn, 200) == %{
1039 test "it works with multiple filters" do
1040 admin = insert(:user, nickname: "john", is_admin: true)
1041 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1043 insert(:user, nickname: "ken", local: true, deactivated: true)
1044 insert(:user, nickname: "bobb", local: false, deactivated: false)
1048 |> assign(:user, admin)
1049 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1051 assert json_response(conn, 200) == %{
1056 "deactivated" => user.deactivated,
1058 "nickname" => user.nickname,
1059 "roles" => %{"admin" => false, "moderator" => false},
1060 "local" => user.local,
1062 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1063 "display_name" => HTML.strip_tags(user.name || user.nickname),
1064 "confirmation_pending" => false
1070 test "it omits relay user", %{admin: admin} do
1071 assert %User{} = Relay.get_actor()
1075 |> assign(:user, admin)
1076 |> get("/api/pleroma/admin/users")
1078 assert json_response(conn, 200) == %{
1083 "deactivated" => admin.deactivated,
1085 "nickname" => admin.nickname,
1086 "roles" => %{"admin" => true, "moderator" => false},
1089 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1090 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1091 "confirmation_pending" => false
1098 test "PATCH /api/pleroma/admin/users/activate" do
1099 admin = insert(:user, is_admin: true)
1100 user_one = insert(:user, deactivated: true)
1101 user_two = insert(:user, deactivated: true)
1105 |> assign(:user, admin)
1107 "/api/pleroma/admin/users/activate",
1108 %{nicknames: [user_one.nickname, user_two.nickname]}
1111 response = json_response(conn, 200)
1112 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1114 log_entry = Repo.one(ModerationLog)
1116 assert ModerationLog.get_log_entry_message(log_entry) ==
1117 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1120 test "PATCH /api/pleroma/admin/users/deactivate" do
1121 admin = insert(:user, is_admin: true)
1122 user_one = insert(:user, deactivated: false)
1123 user_two = insert(:user, deactivated: false)
1127 |> assign(:user, admin)
1129 "/api/pleroma/admin/users/deactivate",
1130 %{nicknames: [user_one.nickname, user_two.nickname]}
1133 response = json_response(conn, 200)
1134 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1136 log_entry = Repo.one(ModerationLog)
1138 assert ModerationLog.get_log_entry_message(log_entry) ==
1139 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1142 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
1143 admin = insert(:user, is_admin: true)
1144 user = insert(:user)
1148 |> assign(:user, admin)
1149 |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1151 assert json_response(conn, 200) ==
1153 "deactivated" => !user.deactivated,
1155 "nickname" => user.nickname,
1156 "roles" => %{"admin" => false, "moderator" => false},
1159 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1160 "display_name" => HTML.strip_tags(user.name || user.nickname),
1161 "confirmation_pending" => false
1164 log_entry = Repo.one(ModerationLog)
1166 assert ModerationLog.get_log_entry_message(log_entry) ==
1167 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1170 describe "POST /api/pleroma/admin/users/invite_token" do
1172 admin = insert(:user, is_admin: true)
1176 |> assign(:user, admin)
1181 test "without options", %{conn: conn} do
1182 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1184 invite_json = json_response(conn, 200)
1185 invite = UserInviteToken.find_by_token!(invite_json["token"])
1187 refute invite.expires_at
1188 refute invite.max_use
1189 assert invite.invite_type == "one_time"
1192 test "with expires_at", %{conn: conn} do
1194 post(conn, "/api/pleroma/admin/users/invite_token", %{
1195 "expires_at" => Date.to_string(Date.utc_today())
1198 invite_json = json_response(conn, 200)
1199 invite = UserInviteToken.find_by_token!(invite_json["token"])
1202 assert invite.expires_at == Date.utc_today()
1203 refute invite.max_use
1204 assert invite.invite_type == "date_limited"
1207 test "with max_use", %{conn: conn} do
1208 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1210 invite_json = json_response(conn, 200)
1211 invite = UserInviteToken.find_by_token!(invite_json["token"])
1213 refute invite.expires_at
1214 assert invite.max_use == 150
1215 assert invite.invite_type == "reusable"
1218 test "with max use and expires_at", %{conn: conn} do
1220 post(conn, "/api/pleroma/admin/users/invite_token", %{
1222 "expires_at" => Date.to_string(Date.utc_today())
1225 invite_json = json_response(conn, 200)
1226 invite = UserInviteToken.find_by_token!(invite_json["token"])
1228 assert invite.expires_at == Date.utc_today()
1229 assert invite.max_use == 150
1230 assert invite.invite_type == "reusable_date_limited"
1234 describe "GET /api/pleroma/admin/users/invites" do
1236 admin = insert(:user, is_admin: true)
1240 |> assign(:user, admin)
1245 test "no invites", %{conn: conn} do
1246 conn = get(conn, "/api/pleroma/admin/users/invites")
1248 assert json_response(conn, 200) == %{"invites" => []}
1251 test "with invite", %{conn: conn} do
1252 {:ok, invite} = UserInviteToken.create_invite()
1254 conn = get(conn, "/api/pleroma/admin/users/invites")
1256 assert json_response(conn, 200) == %{
1259 "expires_at" => nil,
1261 "invite_type" => "one_time",
1263 "token" => invite.token,
1272 describe "POST /api/pleroma/admin/users/revoke_invite" do
1273 test "with token" do
1274 admin = insert(:user, is_admin: true)
1275 {:ok, invite} = UserInviteToken.create_invite()
1279 |> assign(:user, admin)
1280 |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1282 assert json_response(conn, 200) == %{
1283 "expires_at" => nil,
1285 "invite_type" => "one_time",
1287 "token" => invite.token,
1293 test "with invalid token" do
1294 admin = insert(:user, is_admin: true)
1298 |> assign(:user, admin)
1299 |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1301 assert json_response(conn, :not_found) == "Not found"
1305 describe "GET /api/pleroma/admin/reports/:id" do
1306 setup %{conn: conn} do
1307 admin = insert(:user, is_admin: true)
1309 %{conn: assign(conn, :user, admin)}
1312 test "returns report by its id", %{conn: conn} do
1313 [reporter, target_user] = insert_pair(:user)
1314 activity = insert(:note_activity, user: target_user)
1316 {:ok, %{id: report_id}} =
1317 CommonAPI.report(reporter, %{
1318 "account_id" => target_user.id,
1319 "comment" => "I feel offended",
1320 "status_ids" => [activity.id]
1325 |> get("/api/pleroma/admin/reports/#{report_id}")
1326 |> json_response(:ok)
1328 assert response["id"] == report_id
1331 test "returns 404 when report id is invalid", %{conn: conn} do
1332 conn = get(conn, "/api/pleroma/admin/reports/test")
1334 assert json_response(conn, :not_found) == "Not found"
1338 describe "PATCH /api/pleroma/admin/reports" do
1339 setup %{conn: conn} do
1340 admin = insert(:user, is_admin: true)
1341 [reporter, target_user] = insert_pair(:user)
1342 activity = insert(:note_activity, user: target_user)
1344 {:ok, %{id: report_id}} =
1345 CommonAPI.report(reporter, %{
1346 "account_id" => target_user.id,
1347 "comment" => "I feel offended",
1348 "status_ids" => [activity.id]
1351 {:ok, %{id: second_report_id}} =
1352 CommonAPI.report(reporter, %{
1353 "account_id" => target_user.id,
1354 "comment" => "I feel very offended",
1355 "status_ids" => [activity.id]
1359 conn: assign(conn, :user, admin),
1362 second_report_id: second_report_id
1366 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1368 |> patch("/api/pleroma/admin/reports", %{
1370 %{"state" => "resolved", "id" => id}
1373 |> json_response(:no_content)
1375 activity = Activity.get_by_id(id)
1376 assert activity.data["state"] == "resolved"
1378 log_entry = Repo.one(ModerationLog)
1380 assert ModerationLog.get_log_entry_message(log_entry) ==
1381 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1384 test "closes report", %{conn: conn, id: id, admin: admin} do
1386 |> patch("/api/pleroma/admin/reports", %{
1388 %{"state" => "closed", "id" => id}
1391 |> json_response(:no_content)
1393 activity = Activity.get_by_id(id)
1394 assert activity.data["state"] == "closed"
1396 log_entry = Repo.one(ModerationLog)
1398 assert ModerationLog.get_log_entry_message(log_entry) ==
1399 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1402 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1405 |> patch("/api/pleroma/admin/reports", %{
1407 %{"state" => "test", "id" => id}
1411 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1414 test "returns 404 when report is not exist", %{conn: conn} do
1417 |> patch("/api/pleroma/admin/reports", %{
1419 %{"state" => "closed", "id" => "test"}
1423 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1426 test "updates state of multiple reports", %{
1430 second_report_id: second_report_id
1433 |> patch("/api/pleroma/admin/reports", %{
1435 %{"state" => "resolved", "id" => id},
1436 %{"state" => "closed", "id" => second_report_id}
1439 |> json_response(:no_content)
1441 activity = Activity.get_by_id(id)
1442 second_activity = Activity.get_by_id(second_report_id)
1443 assert activity.data["state"] == "resolved"
1444 assert second_activity.data["state"] == "closed"
1446 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1448 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1449 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1451 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1452 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1456 describe "GET /api/pleroma/admin/reports" do
1457 setup %{conn: conn} do
1458 admin = insert(:user, is_admin: true)
1460 %{conn: assign(conn, :user, admin)}
1463 test "returns empty response when no reports created", %{conn: conn} do
1466 |> get("/api/pleroma/admin/reports")
1467 |> json_response(:ok)
1469 assert Enum.empty?(response["reports"])
1470 assert response["total"] == 0
1473 test "returns reports", %{conn: conn} do
1474 [reporter, target_user] = insert_pair(:user)
1475 activity = insert(:note_activity, user: target_user)
1477 {:ok, %{id: report_id}} =
1478 CommonAPI.report(reporter, %{
1479 "account_id" => target_user.id,
1480 "comment" => "I feel offended",
1481 "status_ids" => [activity.id]
1486 |> get("/api/pleroma/admin/reports")
1487 |> json_response(:ok)
1489 [report] = response["reports"]
1491 assert length(response["reports"]) == 1
1492 assert report["id"] == report_id
1494 assert response["total"] == 1
1497 test "returns reports with specified state", %{conn: conn} do
1498 [reporter, target_user] = insert_pair(:user)
1499 activity = insert(:note_activity, user: target_user)
1501 {:ok, %{id: first_report_id}} =
1502 CommonAPI.report(reporter, %{
1503 "account_id" => target_user.id,
1504 "comment" => "I feel offended",
1505 "status_ids" => [activity.id]
1508 {:ok, %{id: second_report_id}} =
1509 CommonAPI.report(reporter, %{
1510 "account_id" => target_user.id,
1511 "comment" => "I don't like this user"
1514 CommonAPI.update_report_state(second_report_id, "closed")
1518 |> get("/api/pleroma/admin/reports", %{
1521 |> json_response(:ok)
1523 [open_report] = response["reports"]
1525 assert length(response["reports"]) == 1
1526 assert open_report["id"] == first_report_id
1528 assert response["total"] == 1
1532 |> get("/api/pleroma/admin/reports", %{
1535 |> json_response(:ok)
1537 [closed_report] = response["reports"]
1539 assert length(response["reports"]) == 1
1540 assert closed_report["id"] == second_report_id
1542 assert response["total"] == 1
1546 |> get("/api/pleroma/admin/reports", %{
1547 "state" => "resolved"
1549 |> json_response(:ok)
1551 assert Enum.empty?(response["reports"])
1552 assert response["total"] == 0
1555 test "returns 403 when requested by a non-admin" do
1556 user = insert(:user)
1560 |> assign(:user, user)
1561 |> get("/api/pleroma/admin/reports")
1563 assert json_response(conn, :forbidden) == %{"error" => "User is not admin."}
1566 test "returns 403 when requested by anonymous" do
1569 |> get("/api/pleroma/admin/reports")
1571 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1575 describe "GET /api/pleroma/admin/grouped_reports" do
1576 setup %{conn: conn} do
1577 admin = insert(:user, is_admin: true)
1578 [reporter, target_user] = insert_pair(:user)
1580 date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1581 date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1582 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1585 insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
1588 insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
1591 insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
1593 {:ok, first_report} =
1594 CommonAPI.report(reporter, %{
1595 "account_id" => target_user.id,
1596 "status_ids" => [first_status.id, second_status.id, third_status.id]
1599 {:ok, second_report} =
1600 CommonAPI.report(reporter, %{
1601 "account_id" => target_user.id,
1602 "status_ids" => [first_status.id, second_status.id]
1605 {:ok, third_report} =
1606 CommonAPI.report(reporter, %{
1607 "account_id" => target_user.id,
1608 "status_ids" => [first_status.id]
1612 conn: assign(conn, :user, admin),
1613 first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
1614 second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
1615 third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
1616 first_status_reports: [first_report, second_report, third_report],
1617 second_status_reports: [first_report, second_report],
1618 third_status_reports: [first_report],
1619 target_user: target_user,
1624 test "returns reports grouped by status", %{
1626 first_status: first_status,
1627 second_status: second_status,
1628 third_status: third_status,
1629 first_status_reports: first_status_reports,
1630 second_status_reports: second_status_reports,
1631 third_status_reports: third_status_reports,
1632 target_user: target_user,
1637 |> get("/api/pleroma/admin/grouped_reports")
1638 |> json_response(:ok)
1640 assert length(response["reports"]) == 3
1642 first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
1644 second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id))
1646 third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id))
1648 assert length(first_group["reports"]) == 3
1649 assert length(second_group["reports"]) == 2
1650 assert length(third_group["reports"]) == 1
1652 assert first_group["date"] ==
1653 Enum.max_by(first_status_reports, fn act ->
1654 NaiveDateTime.from_iso8601!(act.data["published"])
1655 end).data["published"]
1657 assert first_group["status"] ==
1658 stringify_keys(StatusView.render("show.json", %{activity: first_status}))
1660 assert(first_group["account"]["id"] == target_user.id)
1662 assert length(first_group["actors"]) == 1
1663 assert hd(first_group["actors"])["id"] == reporter.id
1665 assert Enum.map(first_group["reports"], & &1["id"]) --
1666 Enum.map(first_status_reports, & &1.id) == []
1668 assert second_group["date"] ==
1669 Enum.max_by(second_status_reports, fn act ->
1670 NaiveDateTime.from_iso8601!(act.data["published"])
1671 end).data["published"]
1673 assert second_group["status"] ==
1674 stringify_keys(StatusView.render("show.json", %{activity: second_status}))
1676 assert second_group["account"]["id"] == target_user.id
1678 assert length(second_group["actors"]) == 1
1679 assert hd(second_group["actors"])["id"] == reporter.id
1681 assert Enum.map(second_group["reports"], & &1["id"]) --
1682 Enum.map(second_status_reports, & &1.id) == []
1684 assert third_group["date"] ==
1685 Enum.max_by(third_status_reports, fn act ->
1686 NaiveDateTime.from_iso8601!(act.data["published"])
1687 end).data["published"]
1689 assert third_group["status"] ==
1690 stringify_keys(StatusView.render("show.json", %{activity: third_status}))
1692 assert third_group["account"]["id"] == target_user.id
1694 assert length(third_group["actors"]) == 1
1695 assert hd(third_group["actors"])["id"] == reporter.id
1697 assert Enum.map(third_group["reports"], & &1["id"]) --
1698 Enum.map(third_status_reports, & &1.id) == []
1702 describe "POST /api/pleroma/admin/reports/:id/respond" do
1703 setup %{conn: conn} do
1704 admin = insert(:user, is_admin: true)
1706 %{conn: assign(conn, :user, admin), admin: admin}
1709 test "returns created dm", %{conn: conn, admin: admin} do
1710 [reporter, target_user] = insert_pair(:user)
1711 activity = insert(:note_activity, user: target_user)
1713 {:ok, %{id: report_id}} =
1714 CommonAPI.report(reporter, %{
1715 "account_id" => target_user.id,
1716 "comment" => "I feel offended",
1717 "status_ids" => [activity.id]
1722 |> post("/api/pleroma/admin/reports/#{report_id}/respond", %{
1723 "status" => "I will check it out"
1725 |> json_response(:ok)
1727 recipients = Enum.map(response["mentions"], & &1["username"])
1729 assert reporter.nickname in recipients
1730 assert response["content"] == "I will check it out"
1731 assert response["visibility"] == "direct"
1733 log_entry = Repo.one(ModerationLog)
1735 assert ModerationLog.get_log_entry_message(log_entry) ==
1736 "@#{admin.nickname} responded with 'I will check it out' to report ##{
1741 test "returns 400 when status is missing", %{conn: conn} do
1742 conn = post(conn, "/api/pleroma/admin/reports/test/respond")
1744 assert json_response(conn, :bad_request) == "Invalid parameters"
1747 test "returns 404 when report id is invalid", %{conn: conn} do
1749 post(conn, "/api/pleroma/admin/reports/test/respond", %{
1753 assert json_response(conn, :not_found) == "Not found"
1757 describe "PUT /api/pleroma/admin/statuses/:id" do
1758 setup %{conn: conn} do
1759 admin = insert(:user, is_admin: true)
1760 activity = insert(:note_activity)
1762 %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
1765 test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
1768 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
1769 |> json_response(:ok)
1771 assert response["sensitive"]
1773 log_entry = Repo.one(ModerationLog)
1775 assert ModerationLog.get_log_entry_message(log_entry) ==
1776 "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
1780 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
1781 |> json_response(:ok)
1783 refute response["sensitive"]
1786 test "change visibility flag", %{conn: conn, id: id, admin: admin} do
1789 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
1790 |> json_response(:ok)
1792 assert response["visibility"] == "public"
1794 log_entry = Repo.one(ModerationLog)
1796 assert ModerationLog.get_log_entry_message(log_entry) ==
1797 "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
1801 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
1802 |> json_response(:ok)
1804 assert response["visibility"] == "private"
1808 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
1809 |> json_response(:ok)
1811 assert response["visibility"] == "unlisted"
1814 test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
1817 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})
1819 assert json_response(conn, :bad_request) == "Unsupported visibility"
1823 describe "DELETE /api/pleroma/admin/statuses/:id" do
1824 setup %{conn: conn} do
1825 admin = insert(:user, is_admin: true)
1826 activity = insert(:note_activity)
1828 %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
1831 test "deletes status", %{conn: conn, id: id, admin: admin} do
1833 |> delete("/api/pleroma/admin/statuses/#{id}")
1834 |> json_response(:ok)
1836 refute Activity.get_by_id(id)
1838 log_entry = Repo.one(ModerationLog)
1840 assert ModerationLog.get_log_entry_message(log_entry) ==
1841 "@#{admin.nickname} deleted status ##{id}"
1844 test "returns error when status is not exist", %{conn: conn} do
1847 |> delete("/api/pleroma/admin/statuses/test")
1849 assert json_response(conn, :bad_request) == "Could not delete"
1853 describe "GET /api/pleroma/admin/config" do
1854 setup %{conn: conn} do
1855 admin = insert(:user, is_admin: true)
1857 %{conn: assign(conn, :user, admin)}
1860 test "without any settings in db", %{conn: conn} do
1861 conn = get(conn, "/api/pleroma/admin/config")
1863 assert json_response(conn, 200) == %{"configs" => []}
1866 test "with settings in db", %{conn: conn} do
1867 config1 = insert(:config)
1868 config2 = insert(:config)
1870 conn = get(conn, "/api/pleroma/admin/config")
1883 } = json_response(conn, 200)
1885 assert key1 == config1.key
1886 assert key2 == config2.key
1890 describe "POST /api/pleroma/admin/config" do
1891 setup %{conn: conn} do
1892 admin = insert(:user, is_admin: true)
1894 temp_file = "config/test.exported_from_db.secret.exs"
1897 Application.delete_env(:pleroma, :key1)
1898 Application.delete_env(:pleroma, :key2)
1899 Application.delete_env(:pleroma, :key3)
1900 Application.delete_env(:pleroma, :key4)
1901 Application.delete_env(:pleroma, :keyaa1)
1902 Application.delete_env(:pleroma, :keyaa2)
1903 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1904 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1905 :ok = File.rm(temp_file)
1908 %{conn: assign(conn, :user, admin)}
1911 clear_config([:instance, :dynamic_configuration]) do
1912 Pleroma.Config.put([:instance, :dynamic_configuration], true)
1915 test "create new config setting in db", %{conn: conn} do
1917 post(conn, "/api/pleroma/admin/config", %{
1919 %{group: "pleroma", key: "key1", value: "value1"},
1922 key: "Ueberauth.Strategy.Twitter.OAuth",
1923 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1929 ":nested_1" => "nested_value1",
1931 %{":nested_22" => "nested_value222"},
1932 %{":nested_33" => %{":nested_44" => "nested_444"}}
1940 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1941 %{"nested_4" => true}
1947 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
1952 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
1957 assert json_response(conn, 200) == %{
1960 "group" => "pleroma",
1965 "group" => "ueberauth",
1966 "key" => "Ueberauth.Strategy.Twitter.OAuth",
1967 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}]
1970 "group" => "pleroma",
1973 ":nested_1" => "nested_value1",
1975 %{":nested_22" => "nested_value222"},
1976 %{":nested_33" => %{":nested_44" => "nested_444"}}
1981 "group" => "pleroma",
1984 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1985 %{"nested_4" => true}
1989 "group" => "pleroma",
1991 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}
1996 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
2001 assert Application.get_env(:pleroma, :key1) == "value1"
2003 assert Application.get_env(:pleroma, :key2) == %{
2004 nested_1: "nested_value1",
2006 %{nested_22: "nested_value222"},
2007 %{nested_33: %{nested_44: "nested_444"}}
2011 assert Application.get_env(:pleroma, :key3) == [
2012 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
2013 %{"nested_4" => true}
2016 assert Application.get_env(:pleroma, :key4) == %{
2017 "endpoint" => "https://example.com",
2021 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
2024 test "update config setting & delete", %{conn: conn} do
2025 config1 = insert(:config, key: "keyaa1")
2026 config2 = insert(:config, key: "keyaa2")
2030 key: "Ueberauth.Strategy.Microsoft.OAuth",
2031 value: :erlang.term_to_binary([])
2035 post(conn, "/api/pleroma/admin/config", %{
2037 %{group: config1.group, key: config1.key, value: "another_value"},
2038 %{group: config2.group, key: config2.key, delete: "true"},
2041 key: "Ueberauth.Strategy.Microsoft.OAuth",
2047 assert json_response(conn, 200) == %{
2050 "group" => "pleroma",
2051 "key" => config1.key,
2052 "value" => "another_value"
2057 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2058 refute Application.get_env(:pleroma, :keyaa2)
2061 test "common config example", %{conn: conn} do
2063 post(conn, "/api/pleroma/admin/config", %{
2066 "group" => "pleroma",
2067 "key" => "Pleroma.Captcha.NotReal",
2069 %{"tuple" => [":enabled", false]},
2070 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2071 %{"tuple" => [":seconds_valid", 60]},
2072 %{"tuple" => [":path", ""]},
2073 %{"tuple" => [":key1", nil]},
2074 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2075 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2076 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2077 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2078 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}
2084 assert json_response(conn, 200) == %{
2087 "group" => "pleroma",
2088 "key" => "Pleroma.Captcha.NotReal",
2090 %{"tuple" => [":enabled", false]},
2091 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2092 %{"tuple" => [":seconds_valid", 60]},
2093 %{"tuple" => [":path", ""]},
2094 %{"tuple" => [":key1", nil]},
2095 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2096 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2097 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2098 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2099 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}
2106 test "tuples with more than two values", %{conn: conn} do
2108 post(conn, "/api/pleroma/admin/config", %{
2111 "group" => "pleroma",
2112 "key" => "Pleroma.Web.Endpoint.NotReal",
2128 "/api/v1/streaming",
2129 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2136 "Phoenix.Endpoint.CowboyWebSocket",
2139 "Phoenix.Transports.WebSocket",
2142 "Pleroma.Web.Endpoint",
2143 "Pleroma.Web.UserSocket",
2154 "Phoenix.Endpoint.Cowboy2Handler",
2155 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2172 assert json_response(conn, 200) == %{
2175 "group" => "pleroma",
2176 "key" => "Pleroma.Web.Endpoint.NotReal",
2192 "/api/v1/streaming",
2193 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2200 "Phoenix.Endpoint.CowboyWebSocket",
2203 "Phoenix.Transports.WebSocket",
2206 "Pleroma.Web.Endpoint",
2207 "Pleroma.Web.UserSocket",
2218 "Phoenix.Endpoint.Cowboy2Handler",
2219 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2237 test "settings with nesting map", %{conn: conn} do
2239 post(conn, "/api/pleroma/admin/config", %{
2242 "group" => "pleroma",
2245 %{"tuple" => [":key2", "some_val"]},
2250 ":max_options" => 20,
2251 ":max_option_chars" => 200,
2252 ":min_expiration" => 0,
2253 ":max_expiration" => 31_536_000,
2255 ":max_options" => 20,
2256 ":max_option_chars" => 200,
2257 ":min_expiration" => 0,
2258 ":max_expiration" => 31_536_000
2268 assert json_response(conn, 200) ==
2272 "group" => "pleroma",
2275 %{"tuple" => [":key2", "some_val"]},
2280 ":max_expiration" => 31_536_000,
2281 ":max_option_chars" => 200,
2282 ":max_options" => 20,
2283 ":min_expiration" => 0,
2285 ":max_expiration" => 31_536_000,
2286 ":max_option_chars" => 200,
2287 ":max_options" => 20,
2288 ":min_expiration" => 0
2299 test "value as map", %{conn: conn} do
2301 post(conn, "/api/pleroma/admin/config", %{
2304 "group" => "pleroma",
2306 "value" => %{"key" => "some_val"}
2311 assert json_response(conn, 200) ==
2315 "group" => "pleroma",
2317 "value" => %{"key" => "some_val"}
2323 test "dispatch setting", %{conn: conn} do
2325 post(conn, "/api/pleroma/admin/config", %{
2328 "group" => "pleroma",
2329 "key" => "Pleroma.Web.Endpoint.NotReal",
2335 %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
2336 %{"tuple" => [":dispatch", ["{:_,
2338 {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
2339 {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket,
2340 {Phoenix.Transports.WebSocket,
2341 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}},
2342 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
2353 "{:_, [{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, " <>
2354 "{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, {Phoenix.Transports.WebSocket, " <>
2355 "{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, " <>
2356 "{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}]}"
2358 assert json_response(conn, 200) == %{
2361 "group" => "pleroma",
2362 "key" => "Pleroma.Web.Endpoint.NotReal",
2368 %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
2386 test "queues key as atom", %{conn: conn} do
2388 post(conn, "/api/pleroma/admin/config", %{
2394 %{"tuple" => [":federator_incoming", 50]},
2395 %{"tuple" => [":federator_outgoing", 50]},
2396 %{"tuple" => [":web_push", 50]},
2397 %{"tuple" => [":mailer", 10]},
2398 %{"tuple" => [":transmogrifier", 20]},
2399 %{"tuple" => [":scheduled_activities", 10]},
2400 %{"tuple" => [":background", 5]}
2406 assert json_response(conn, 200) == %{
2412 %{"tuple" => [":federator_incoming", 50]},
2413 %{"tuple" => [":federator_outgoing", 50]},
2414 %{"tuple" => [":web_push", 50]},
2415 %{"tuple" => [":mailer", 10]},
2416 %{"tuple" => [":transmogrifier", 20]},
2417 %{"tuple" => [":scheduled_activities", 10]},
2418 %{"tuple" => [":background", 5]}
2425 test "delete part of settings by atom subkeys", %{conn: conn} do
2429 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2433 post(conn, "/api/pleroma/admin/config", %{
2436 group: config.group,
2438 subkeys: [":subkey1", ":subkey3"],
2445 json_response(conn, 200) == %{
2448 "group" => "pleroma",
2450 "value" => [%{"tuple" => [":subkey2", "val2"]}]
2458 describe "config mix tasks run" do
2459 setup %{conn: conn} do
2460 admin = insert(:user, is_admin: true)
2462 temp_file = "config/test.exported_from_db.secret.exs"
2464 Mix.shell(Mix.Shell.Quiet)
2467 Mix.shell(Mix.Shell.IO)
2468 :ok = File.rm(temp_file)
2471 %{conn: assign(conn, :user, admin), admin: admin}
2474 clear_config([:instance, :dynamic_configuration]) do
2475 Pleroma.Config.put([:instance, :dynamic_configuration], true)
2478 clear_config([:feed, :post_title]) do
2479 Pleroma.Config.put([:feed, :post_title], %{max_length: 100, omission: "…"})
2482 test "transfer settings to DB and to file", %{conn: conn, admin: admin} do
2483 assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
2484 conn = get(conn, "/api/pleroma/admin/config/migrate_to_db")
2485 assert json_response(conn, 200) == %{}
2486 assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) > 0
2490 |> assign(:user, admin)
2491 |> get("/api/pleroma/admin/config/migrate_from_db")
2493 assert json_response(conn, 200) == %{}
2494 assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
2498 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2500 admin = insert(:user, is_admin: true)
2501 user = insert(:user)
2503 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2504 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2505 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2507 insert(:note_activity, user: user, published: date1)
2508 insert(:note_activity, user: user, published: date2)
2509 insert(:note_activity, user: user, published: date3)
2513 |> assign(:user, admin)
2515 {:ok, conn: conn, user: user}
2518 test "renders user's statuses", %{conn: conn, user: user} do
2519 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2521 assert json_response(conn, 200) |> length() == 3
2524 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2525 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2527 assert json_response(conn, 200) |> length() == 2
2530 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2531 {:ok, _private_status} =
2532 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2534 {:ok, _public_status} =
2535 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2537 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2539 assert json_response(conn, 200) |> length() == 4
2542 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2543 {:ok, _private_status} =
2544 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2546 {:ok, _public_status} =
2547 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2549 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2551 assert json_response(conn, 200) |> length() == 5
2555 describe "GET /api/pleroma/admin/moderation_log" do
2556 setup %{conn: conn} do
2557 admin = insert(:user, is_admin: true)
2558 moderator = insert(:user, is_moderator: true)
2560 %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator}
2563 test "returns the log", %{conn: conn, admin: admin} do
2564 Repo.insert(%ModerationLog{
2568 "nickname" => admin.nickname,
2571 action: "relay_follow",
2572 target: "https://example.org/relay"
2574 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2577 Repo.insert(%ModerationLog{
2581 "nickname" => admin.nickname,
2584 action: "relay_unfollow",
2585 target: "https://example.org/relay"
2587 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2590 conn = get(conn, "/api/pleroma/admin/moderation_log")
2592 response = json_response(conn, 200)
2593 [first_entry, second_entry] = response["items"]
2595 assert response["total"] == 2
2596 assert first_entry["data"]["action"] == "relay_unfollow"
2598 assert first_entry["message"] ==
2599 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2601 assert second_entry["data"]["action"] == "relay_follow"
2603 assert second_entry["message"] ==
2604 "@#{admin.nickname} followed relay: https://example.org/relay"
2607 test "returns the log with pagination", %{conn: conn, admin: admin} do
2608 Repo.insert(%ModerationLog{
2612 "nickname" => admin.nickname,
2615 action: "relay_follow",
2616 target: "https://example.org/relay"
2618 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2621 Repo.insert(%ModerationLog{
2625 "nickname" => admin.nickname,
2628 action: "relay_unfollow",
2629 target: "https://example.org/relay"
2631 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2634 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
2636 response1 = json_response(conn1, 200)
2637 [first_entry] = response1["items"]
2639 assert response1["total"] == 2
2640 assert response1["items"] |> length() == 1
2641 assert first_entry["data"]["action"] == "relay_unfollow"
2643 assert first_entry["message"] ==
2644 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2646 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
2648 response2 = json_response(conn2, 200)
2649 [second_entry] = response2["items"]
2651 assert response2["total"] == 2
2652 assert response2["items"] |> length() == 1
2653 assert second_entry["data"]["action"] == "relay_follow"
2655 assert second_entry["message"] ==
2656 "@#{admin.nickname} followed relay: https://example.org/relay"
2659 test "filters log by date", %{conn: conn, admin: admin} do
2660 first_date = "2017-08-15T15:47:06Z"
2661 second_date = "2017-08-20T15:47:06Z"
2663 Repo.insert(%ModerationLog{
2667 "nickname" => admin.nickname,
2670 action: "relay_follow",
2671 target: "https://example.org/relay"
2673 inserted_at: NaiveDateTime.from_iso8601!(first_date)
2676 Repo.insert(%ModerationLog{
2680 "nickname" => admin.nickname,
2683 action: "relay_unfollow",
2684 target: "https://example.org/relay"
2686 inserted_at: NaiveDateTime.from_iso8601!(second_date)
2692 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
2695 response1 = json_response(conn1, 200)
2696 [first_entry] = response1["items"]
2698 assert response1["total"] == 1
2699 assert first_entry["data"]["action"] == "relay_unfollow"
2701 assert first_entry["message"] ==
2702 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2705 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
2706 Repo.insert(%ModerationLog{
2710 "nickname" => admin.nickname,
2713 action: "relay_follow",
2714 target: "https://example.org/relay"
2718 Repo.insert(%ModerationLog{
2721 "id" => moderator.id,
2722 "nickname" => moderator.nickname,
2725 action: "relay_unfollow",
2726 target: "https://example.org/relay"
2730 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
2732 response1 = json_response(conn1, 200)
2733 [first_entry] = response1["items"]
2735 assert response1["total"] == 1
2736 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
2739 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
2740 ModerationLog.insert_log(%{
2742 action: "relay_follow",
2743 target: "https://example.org/relay"
2746 ModerationLog.insert_log(%{
2748 action: "relay_unfollow",
2749 target: "https://example.org/relay"
2752 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
2754 response1 = json_response(conn1, 200)
2755 [first_entry] = response1["items"]
2757 assert response1["total"] == 1
2759 assert get_in(first_entry, ["data", "message"]) ==
2760 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
2764 describe "PATCH /users/:nickname/force_password_reset" do
2765 setup %{conn: conn} do
2766 admin = insert(:user, is_admin: true)
2767 user = insert(:user)
2769 %{conn: assign(conn, :user, admin), admin: admin, user: user}
2772 test "sets password_reset_pending to true", %{admin: admin, user: user} do
2773 assert user.password_reset_pending == false
2777 |> assign(:user, admin)
2778 |> patch("/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
2780 assert json_response(conn, 204) == ""
2782 ObanHelpers.perform_all()
2784 assert User.get_by_id(user.id).password_reset_pending == true
2788 describe "relays" do
2789 setup %{conn: conn} do
2790 admin = insert(:user, is_admin: true)
2792 %{conn: assign(conn, :user, admin), admin: admin}
2795 test "POST /relay", %{admin: admin} do
2798 |> assign(:user, admin)
2799 |> post("/api/pleroma/admin/relay", %{
2800 relay_url: "http://mastodon.example.org/users/admin"
2803 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2805 log_entry = Repo.one(ModerationLog)
2807 assert ModerationLog.get_log_entry_message(log_entry) ==
2808 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2811 test "GET /relay", %{admin: admin} do
2812 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
2814 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
2815 |> Enum.each(fn ap_id ->
2816 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
2817 User.follow(relay_user, user)
2822 |> assign(:user, admin)
2823 |> get("/api/pleroma/admin/relay")
2825 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
2828 test "DELETE /relay", %{admin: admin} do
2830 |> assign(:user, admin)
2831 |> post("/api/pleroma/admin/relay", %{
2832 relay_url: "http://mastodon.example.org/users/admin"
2837 |> assign(:user, admin)
2838 |> delete("/api/pleroma/admin/relay", %{
2839 relay_url: "http://mastodon.example.org/users/admin"
2842 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2844 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
2846 assert ModerationLog.get_log_entry_message(log_entry_one) ==
2847 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2849 assert ModerationLog.get_log_entry_message(log_entry_two) ==
2850 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
2854 describe "instances" do
2855 test "GET /instances/:instance/statuses" do
2856 admin = insert(:user, is_admin: true)
2857 user = insert(:user, local: false, nickname: "archaeme@archae.me")
2858 user2 = insert(:user, local: false, nickname: "test@test.com")
2859 insert_pair(:note_activity, user: user)
2860 insert(:note_activity, user: user2)
2864 |> assign(:user, admin)
2865 |> get("/api/pleroma/admin/instances/archae.me/statuses")
2867 response = json_response(conn, 200)
2869 assert length(response) == 2
2873 |> assign(:user, admin)
2874 |> get("/api/pleroma/admin/instances/test.com/statuses")
2876 response = json_response(conn, 200)
2878 assert length(response) == 1
2882 |> assign(:user, admin)
2883 |> get("/api/pleroma/admin/instances/nonexistent.com/statuses")
2885 response = json_response(conn, 200)
2887 assert length(response) == 0
2891 describe "PATCH /confirm_email" do
2892 setup %{conn: conn} do
2893 admin = insert(:user, is_admin: true)
2895 %{conn: assign(conn, :user, admin), admin: admin}
2898 test "it confirms emails of two users", %{admin: admin} do
2899 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2901 assert first_user.confirmation_pending == true
2902 assert second_user.confirmation_pending == true
2905 |> assign(:user, admin)
2906 |> patch("/api/pleroma/admin/users/confirm_email", %{
2908 first_user.nickname,
2909 second_user.nickname
2913 assert first_user.confirmation_pending == true
2914 assert second_user.confirmation_pending == true
2916 log_entry = Repo.one(ModerationLog)
2918 assert ModerationLog.get_log_entry_message(log_entry) ==
2919 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
2920 second_user.nickname
2925 describe "PATCH /resend_confirmation_email" do
2926 setup %{conn: conn} do
2927 admin = insert(:user, is_admin: true)
2929 %{conn: assign(conn, :user, admin), admin: admin}
2932 test "it resend emails for two users", %{admin: admin} do
2933 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2936 |> assign(:user, admin)
2937 |> patch("/api/pleroma/admin/users/resend_confirmation_email", %{
2939 first_user.nickname,
2940 second_user.nickname
2944 log_entry = Repo.one(ModerationLog)
2946 assert ModerationLog.get_log_entry_message(log_entry) ==
2947 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
2948 second_user.nickname
2954 # Needed for testing
2955 defmodule Pleroma.Web.Endpoint.NotReal do
2958 defmodule Pleroma.Captcha.NotReal do