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
10 import ExUnit.CaptureLog
12 alias Pleroma.Activity
14 alias Pleroma.ConfigDB
16 alias Pleroma.ModerationLog
18 alias Pleroma.ReportNote
19 alias Pleroma.Tests.ObanHelpers
21 alias Pleroma.UserInviteToken
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
151 |> put_req_header("accept", "application/json")
152 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
154 log_entry = Repo.one(ModerationLog)
156 assert ModerationLog.get_log_entry_message(log_entry) ==
157 "@#{admin.nickname} deleted users: @#{user.nickname}"
159 assert json_response(conn, 200) == user.nickname
162 test "multiple users", %{admin: admin, conn: conn} do
163 user_one = insert(:user)
164 user_two = insert(:user)
168 |> put_req_header("accept", "application/json")
169 |> delete("/api/pleroma/admin/users", %{
170 nicknames: [user_one.nickname, user_two.nickname]
173 log_entry = Repo.one(ModerationLog)
175 assert ModerationLog.get_log_entry_message(log_entry) ==
176 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
178 response = json_response(conn, 200)
179 assert response -- [user_one.nickname, user_two.nickname] == []
183 describe "/api/pleroma/admin/users" do
184 test "Create", %{conn: conn} do
187 |> put_req_header("accept", "application/json")
188 |> post("/api/pleroma/admin/users", %{
191 "nickname" => "lain",
192 "email" => "lain@example.org",
196 "nickname" => "lain2",
197 "email" => "lain2@example.org",
203 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
204 assert response == ["success", "success"]
206 log_entry = Repo.one(ModerationLog)
208 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
211 test "Cannot create user with existing email", %{conn: conn} do
216 |> put_req_header("accept", "application/json")
217 |> post("/api/pleroma/admin/users", %{
220 "nickname" => "lain",
221 "email" => user.email,
227 assert json_response(conn, 409) == [
231 "email" => user.email,
234 "error" => "email has already been taken",
240 test "Cannot create user with existing nickname", %{conn: conn} do
245 |> put_req_header("accept", "application/json")
246 |> post("/api/pleroma/admin/users", %{
249 "nickname" => user.nickname,
250 "email" => "someuser@plerama.social",
256 assert json_response(conn, 409) == [
260 "email" => "someuser@plerama.social",
261 "nickname" => user.nickname
263 "error" => "nickname has already been taken",
269 test "Multiple user creation works in transaction", %{conn: conn} do
274 |> put_req_header("accept", "application/json")
275 |> post("/api/pleroma/admin/users", %{
278 "nickname" => "newuser",
279 "email" => "newuser@pleroma.social",
283 "nickname" => "lain",
284 "email" => user.email,
290 assert json_response(conn, 409) == [
294 "email" => user.email,
297 "error" => "email has already been taken",
303 "email" => "newuser@pleroma.social",
304 "nickname" => "newuser"
311 assert User.get_by_nickname("newuser") === nil
315 describe "/api/pleroma/admin/users/:nickname" do
316 test "Show", %{conn: conn} do
319 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
322 "deactivated" => false,
323 "id" => to_string(user.id),
325 "nickname" => user.nickname,
326 "roles" => %{"admin" => false, "moderator" => false},
328 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
329 "display_name" => HTML.strip_tags(user.name || user.nickname),
330 "confirmation_pending" => false
333 assert expected == json_response(conn, 200)
336 test "when the user doesn't exist", %{conn: conn} do
339 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
341 assert "Not found" == json_response(conn, 404)
345 describe "/api/pleroma/admin/users/follow" do
346 test "allows to force-follow another user", %{admin: admin, conn: conn} do
348 follower = insert(:user)
351 |> put_req_header("accept", "application/json")
352 |> post("/api/pleroma/admin/users/follow", %{
353 "follower" => follower.nickname,
354 "followed" => user.nickname
357 user = User.get_cached_by_id(user.id)
358 follower = User.get_cached_by_id(follower.id)
360 assert User.following?(follower, user)
362 log_entry = Repo.one(ModerationLog)
364 assert ModerationLog.get_log_entry_message(log_entry) ==
365 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
369 describe "/api/pleroma/admin/users/unfollow" do
370 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
372 follower = insert(:user)
374 User.follow(follower, user)
377 |> put_req_header("accept", "application/json")
378 |> post("/api/pleroma/admin/users/unfollow", %{
379 "follower" => follower.nickname,
380 "followed" => user.nickname
383 user = User.get_cached_by_id(user.id)
384 follower = User.get_cached_by_id(follower.id)
386 refute User.following?(follower, user)
388 log_entry = Repo.one(ModerationLog)
390 assert ModerationLog.get_log_entry_message(log_entry) ==
391 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
395 describe "PUT /api/pleroma/admin/users/tag" do
396 setup %{conn: conn} do
397 user1 = insert(:user, %{tags: ["x"]})
398 user2 = insert(:user, %{tags: ["y"]})
399 user3 = insert(:user, %{tags: ["unchanged"]})
403 |> put_req_header("accept", "application/json")
405 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
406 "#{user2.nickname}&tags[]=foo&tags[]=bar"
409 %{conn: conn, user1: user1, user2: user2, user3: user3}
412 test "it appends specified tags to users with specified nicknames", %{
418 assert json_response(conn, :no_content)
419 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
420 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
422 log_entry = Repo.one(ModerationLog)
425 [user1.nickname, user2.nickname]
426 |> Enum.map(&"@#{&1}")
429 tags = ["foo", "bar"] |> Enum.join(", ")
431 assert ModerationLog.get_log_entry_message(log_entry) ==
432 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
435 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
436 assert json_response(conn, :no_content)
437 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
441 describe "DELETE /api/pleroma/admin/users/tag" do
442 setup %{conn: conn} do
443 user1 = insert(:user, %{tags: ["x"]})
444 user2 = insert(:user, %{tags: ["y", "z"]})
445 user3 = insert(:user, %{tags: ["unchanged"]})
449 |> put_req_header("accept", "application/json")
451 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
452 "#{user2.nickname}&tags[]=x&tags[]=z"
455 %{conn: conn, user1: user1, user2: user2, user3: user3}
458 test "it removes specified tags from users with specified nicknames", %{
464 assert json_response(conn, :no_content)
465 assert User.get_cached_by_id(user1.id).tags == []
466 assert User.get_cached_by_id(user2.id).tags == ["y"]
468 log_entry = Repo.one(ModerationLog)
471 [user1.nickname, user2.nickname]
472 |> Enum.map(&"@#{&1}")
475 tags = ["x", "z"] |> Enum.join(", ")
477 assert ModerationLog.get_log_entry_message(log_entry) ==
478 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
481 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
482 assert json_response(conn, :no_content)
483 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
487 describe "/api/pleroma/admin/users/:nickname/permission_group" do
488 test "GET is giving user_info", %{admin: admin, conn: conn} do
491 |> put_req_header("accept", "application/json")
492 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
494 assert json_response(conn, 200) == %{
496 "is_moderator" => false
500 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
505 |> put_req_header("accept", "application/json")
506 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
508 assert json_response(conn, 200) == %{
512 log_entry = Repo.one(ModerationLog)
514 assert ModerationLog.get_log_entry_message(log_entry) ==
515 "@#{admin.nickname} made @#{user.nickname} admin"
518 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
519 user_one = insert(:user)
520 user_two = insert(:user)
524 |> put_req_header("accept", "application/json")
525 |> post("/api/pleroma/admin/users/permission_group/admin", %{
526 nicknames: [user_one.nickname, user_two.nickname]
529 assert json_response(conn, 200) == %{"is_admin" => true}
531 log_entry = Repo.one(ModerationLog)
533 assert ModerationLog.get_log_entry_message(log_entry) ==
534 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
537 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
538 user = insert(:user, is_admin: true)
542 |> put_req_header("accept", "application/json")
543 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
545 assert json_response(conn, 200) == %{"is_admin" => false}
547 log_entry = Repo.one(ModerationLog)
549 assert ModerationLog.get_log_entry_message(log_entry) ==
550 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
553 test "/:right DELETE, can remove from a permission group (multiple)", %{
557 user_one = insert(:user, is_admin: true)
558 user_two = insert(:user, is_admin: true)
562 |> put_req_header("accept", "application/json")
563 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
564 nicknames: [user_one.nickname, user_two.nickname]
567 assert json_response(conn, 200) == %{"is_admin" => false}
569 log_entry = Repo.one(ModerationLog)
571 assert ModerationLog.get_log_entry_message(log_entry) ==
572 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
578 describe "POST /api/pleroma/admin/email_invite, with valid config" do
579 setup do: clear_config([:instance, :registrations_open], false)
580 setup do: clear_config([:instance, :invites_enabled], true)
582 test "sends invitation and returns 204", %{admin: admin, conn: conn} do
583 recipient_email = "foo@bar.com"
584 recipient_name = "J. D."
589 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
592 assert json_response(conn, :no_content)
594 token_record = List.last(Repo.all(Pleroma.UserInviteToken))
596 refute token_record.used
598 notify_email = Config.get([:instance, :notify_email])
599 instance_name = Config.get([:instance, :name])
602 Pleroma.Emails.UserEmail.user_invitation_email(
609 Swoosh.TestAssertions.assert_email_sent(
610 from: {instance_name, notify_email},
611 to: {recipient_name, recipient_email},
612 html_body: email.html_body
616 test "it returns 403 if requested by a non-admin" do
617 non_admin_user = insert(:user)
618 token = insert(:oauth_token, user: non_admin_user)
622 |> assign(:user, non_admin_user)
623 |> assign(:token, token)
624 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
626 assert json_response(conn, :forbidden)
630 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
631 setup do: clear_config([:instance, :registrations_open])
632 setup do: clear_config([:instance, :invites_enabled])
634 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
635 Config.put([:instance, :registrations_open], false)
636 Config.put([:instance, :invites_enabled], false)
638 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
640 assert json_response(conn, :internal_server_error)
643 test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
644 Config.put([:instance, :registrations_open], true)
645 Config.put([:instance, :invites_enabled], true)
647 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
649 assert json_response(conn, :internal_server_error)
653 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
658 |> put_req_header("accept", "application/json")
659 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
661 resp = json_response(conn, 200)
663 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
666 describe "GET /api/pleroma/admin/users" do
667 test "renders users array for the first page", %{conn: conn, admin: admin} do
668 user = insert(:user, local: false, tags: ["foo", "bar"])
669 conn = get(conn, "/api/pleroma/admin/users?page=1")
674 "deactivated" => admin.deactivated,
676 "nickname" => admin.nickname,
677 "roles" => %{"admin" => true, "moderator" => false},
680 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
681 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
682 "confirmation_pending" => false
685 "deactivated" => user.deactivated,
687 "nickname" => user.nickname,
688 "roles" => %{"admin" => false, "moderator" => false},
690 "tags" => ["foo", "bar"],
691 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
692 "display_name" => HTML.strip_tags(user.name || user.nickname),
693 "confirmation_pending" => false
696 |> Enum.sort_by(& &1["nickname"])
698 assert json_response(conn, 200) == %{
705 test "renders empty array for the second page", %{conn: conn} do
708 conn = get(conn, "/api/pleroma/admin/users?page=2")
710 assert json_response(conn, 200) == %{
717 test "regular search", %{conn: conn} do
718 user = insert(:user, nickname: "bob")
720 conn = get(conn, "/api/pleroma/admin/users?query=bo")
722 assert json_response(conn, 200) == %{
727 "deactivated" => user.deactivated,
729 "nickname" => user.nickname,
730 "roles" => %{"admin" => false, "moderator" => false},
733 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
734 "display_name" => HTML.strip_tags(user.name || user.nickname),
735 "confirmation_pending" => false
741 test "search by domain", %{conn: conn} do
742 user = insert(:user, nickname: "nickname@domain.com")
745 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
747 assert json_response(conn, 200) == %{
752 "deactivated" => user.deactivated,
754 "nickname" => user.nickname,
755 "roles" => %{"admin" => false, "moderator" => false},
758 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
759 "display_name" => HTML.strip_tags(user.name || user.nickname),
760 "confirmation_pending" => false
766 test "search by full nickname", %{conn: conn} do
767 user = insert(:user, nickname: "nickname@domain.com")
770 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
772 assert json_response(conn, 200) == %{
777 "deactivated" => user.deactivated,
779 "nickname" => user.nickname,
780 "roles" => %{"admin" => false, "moderator" => false},
783 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
784 "display_name" => HTML.strip_tags(user.name || user.nickname),
785 "confirmation_pending" => false
791 test "search by display name", %{conn: conn} do
792 user = insert(:user, name: "Display name")
795 conn = get(conn, "/api/pleroma/admin/users?name=display")
797 assert json_response(conn, 200) == %{
802 "deactivated" => user.deactivated,
804 "nickname" => user.nickname,
805 "roles" => %{"admin" => false, "moderator" => false},
808 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
809 "display_name" => HTML.strip_tags(user.name || user.nickname),
810 "confirmation_pending" => false
816 test "search by email", %{conn: conn} do
817 user = insert(:user, email: "email@example.com")
820 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
822 assert json_response(conn, 200) == %{
827 "deactivated" => user.deactivated,
829 "nickname" => user.nickname,
830 "roles" => %{"admin" => false, "moderator" => false},
833 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
834 "display_name" => HTML.strip_tags(user.name || user.nickname),
835 "confirmation_pending" => false
841 test "regular search with page size", %{conn: conn} do
842 user = insert(:user, nickname: "aalice")
843 user2 = insert(:user, nickname: "alice")
845 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
847 assert json_response(conn1, 200) == %{
852 "deactivated" => user.deactivated,
854 "nickname" => user.nickname,
855 "roles" => %{"admin" => false, "moderator" => false},
858 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
859 "display_name" => HTML.strip_tags(user.name || user.nickname),
860 "confirmation_pending" => false
865 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
867 assert json_response(conn2, 200) == %{
872 "deactivated" => user2.deactivated,
874 "nickname" => user2.nickname,
875 "roles" => %{"admin" => false, "moderator" => false},
878 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
879 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
880 "confirmation_pending" => false
886 test "only local users" do
887 admin = insert(:user, is_admin: true, nickname: "john")
888 token = insert(:oauth_admin_token, user: admin)
889 user = insert(:user, nickname: "bob")
891 insert(:user, nickname: "bobb", local: false)
895 |> assign(:user, admin)
896 |> assign(:token, token)
897 |> get("/api/pleroma/admin/users?query=bo&filters=local")
899 assert json_response(conn, 200) == %{
904 "deactivated" => user.deactivated,
906 "nickname" => user.nickname,
907 "roles" => %{"admin" => false, "moderator" => false},
910 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
911 "display_name" => HTML.strip_tags(user.name || user.nickname),
912 "confirmation_pending" => false
918 test "only local users with no query", %{conn: conn, admin: old_admin} do
919 admin = insert(:user, is_admin: true, nickname: "john")
920 user = insert(:user, nickname: "bob")
922 insert(:user, nickname: "bobb", local: false)
924 conn = get(conn, "/api/pleroma/admin/users?filters=local")
929 "deactivated" => user.deactivated,
931 "nickname" => user.nickname,
932 "roles" => %{"admin" => false, "moderator" => false},
935 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
936 "display_name" => HTML.strip_tags(user.name || user.nickname),
937 "confirmation_pending" => false
940 "deactivated" => admin.deactivated,
942 "nickname" => admin.nickname,
943 "roles" => %{"admin" => true, "moderator" => false},
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" => old_admin.id,
954 "nickname" => old_admin.nickname,
955 "roles" => %{"admin" => true, "moderator" => false},
957 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
958 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
959 "confirmation_pending" => false
962 |> Enum.sort_by(& &1["nickname"])
964 assert json_response(conn, 200) == %{
971 test "load only admins", %{conn: conn, admin: admin} do
972 second_admin = insert(:user, is_admin: true)
976 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
981 "deactivated" => false,
983 "nickname" => admin.nickname,
984 "roles" => %{"admin" => true, "moderator" => false},
985 "local" => admin.local,
987 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
988 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
989 "confirmation_pending" => false
992 "deactivated" => false,
993 "id" => second_admin.id,
994 "nickname" => second_admin.nickname,
995 "roles" => %{"admin" => true, "moderator" => false},
996 "local" => second_admin.local,
998 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
999 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1000 "confirmation_pending" => false
1003 |> Enum.sort_by(& &1["nickname"])
1005 assert json_response(conn, 200) == %{
1012 test "load only moderators", %{conn: conn} do
1013 moderator = insert(:user, is_moderator: true)
1017 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1019 assert json_response(conn, 200) == %{
1024 "deactivated" => false,
1025 "id" => moderator.id,
1026 "nickname" => moderator.nickname,
1027 "roles" => %{"admin" => false, "moderator" => true},
1028 "local" => moderator.local,
1030 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1031 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1032 "confirmation_pending" => false
1038 test "load users with tags list", %{conn: conn} do
1039 user1 = insert(:user, tags: ["first"])
1040 user2 = insert(:user, tags: ["second"])
1044 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1049 "deactivated" => false,
1051 "nickname" => user1.nickname,
1052 "roles" => %{"admin" => false, "moderator" => false},
1053 "local" => user1.local,
1054 "tags" => ["first"],
1055 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1056 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1057 "confirmation_pending" => false
1060 "deactivated" => false,
1062 "nickname" => user2.nickname,
1063 "roles" => %{"admin" => false, "moderator" => false},
1064 "local" => user2.local,
1065 "tags" => ["second"],
1066 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1067 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1068 "confirmation_pending" => false
1071 |> Enum.sort_by(& &1["nickname"])
1073 assert json_response(conn, 200) == %{
1080 test "it works with multiple filters" do
1081 admin = insert(:user, nickname: "john", is_admin: true)
1082 token = insert(:oauth_admin_token, user: admin)
1083 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1085 insert(:user, nickname: "ken", local: true, deactivated: true)
1086 insert(:user, nickname: "bobb", local: false, deactivated: false)
1090 |> assign(:user, admin)
1091 |> assign(:token, token)
1092 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1094 assert json_response(conn, 200) == %{
1099 "deactivated" => user.deactivated,
1101 "nickname" => user.nickname,
1102 "roles" => %{"admin" => false, "moderator" => false},
1103 "local" => user.local,
1105 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1106 "display_name" => HTML.strip_tags(user.name || user.nickname),
1107 "confirmation_pending" => false
1113 test "it omits relay user", %{admin: admin, conn: conn} do
1114 assert %User{} = Relay.get_actor()
1116 conn = get(conn, "/api/pleroma/admin/users")
1118 assert json_response(conn, 200) == %{
1123 "deactivated" => admin.deactivated,
1125 "nickname" => admin.nickname,
1126 "roles" => %{"admin" => true, "moderator" => false},
1129 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1130 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1131 "confirmation_pending" => false
1138 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1139 user_one = insert(:user, deactivated: true)
1140 user_two = insert(:user, deactivated: true)
1145 "/api/pleroma/admin/users/activate",
1146 %{nicknames: [user_one.nickname, user_two.nickname]}
1149 response = json_response(conn, 200)
1150 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1152 log_entry = Repo.one(ModerationLog)
1154 assert ModerationLog.get_log_entry_message(log_entry) ==
1155 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1158 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1159 user_one = insert(:user, deactivated: false)
1160 user_two = insert(:user, deactivated: false)
1165 "/api/pleroma/admin/users/deactivate",
1166 %{nicknames: [user_one.nickname, user_two.nickname]}
1169 response = json_response(conn, 200)
1170 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1172 log_entry = Repo.one(ModerationLog)
1174 assert ModerationLog.get_log_entry_message(log_entry) ==
1175 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1178 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1179 user = insert(:user)
1181 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1183 assert json_response(conn, 200) ==
1185 "deactivated" => !user.deactivated,
1187 "nickname" => user.nickname,
1188 "roles" => %{"admin" => false, "moderator" => false},
1191 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1192 "display_name" => HTML.strip_tags(user.name || user.nickname),
1193 "confirmation_pending" => false
1196 log_entry = Repo.one(ModerationLog)
1198 assert ModerationLog.get_log_entry_message(log_entry) ==
1199 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1202 describe "POST /api/pleroma/admin/users/invite_token" do
1203 test "without options", %{conn: conn} do
1204 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1206 invite_json = json_response(conn, 200)
1207 invite = UserInviteToken.find_by_token!(invite_json["token"])
1209 refute invite.expires_at
1210 refute invite.max_use
1211 assert invite.invite_type == "one_time"
1214 test "with expires_at", %{conn: conn} do
1216 post(conn, "/api/pleroma/admin/users/invite_token", %{
1217 "expires_at" => Date.to_string(Date.utc_today())
1220 invite_json = json_response(conn, 200)
1221 invite = UserInviteToken.find_by_token!(invite_json["token"])
1224 assert invite.expires_at == Date.utc_today()
1225 refute invite.max_use
1226 assert invite.invite_type == "date_limited"
1229 test "with max_use", %{conn: conn} do
1230 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1232 invite_json = json_response(conn, 200)
1233 invite = UserInviteToken.find_by_token!(invite_json["token"])
1235 refute invite.expires_at
1236 assert invite.max_use == 150
1237 assert invite.invite_type == "reusable"
1240 test "with max use and expires_at", %{conn: conn} do
1242 post(conn, "/api/pleroma/admin/users/invite_token", %{
1244 "expires_at" => Date.to_string(Date.utc_today())
1247 invite_json = json_response(conn, 200)
1248 invite = UserInviteToken.find_by_token!(invite_json["token"])
1250 assert invite.expires_at == Date.utc_today()
1251 assert invite.max_use == 150
1252 assert invite.invite_type == "reusable_date_limited"
1256 describe "GET /api/pleroma/admin/users/invites" do
1257 test "no invites", %{conn: conn} do
1258 conn = get(conn, "/api/pleroma/admin/users/invites")
1260 assert json_response(conn, 200) == %{"invites" => []}
1263 test "with invite", %{conn: conn} do
1264 {:ok, invite} = UserInviteToken.create_invite()
1266 conn = get(conn, "/api/pleroma/admin/users/invites")
1268 assert json_response(conn, 200) == %{
1271 "expires_at" => nil,
1273 "invite_type" => "one_time",
1275 "token" => invite.token,
1284 describe "POST /api/pleroma/admin/users/revoke_invite" do
1285 test "with token", %{conn: conn} do
1286 {:ok, invite} = UserInviteToken.create_invite()
1288 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1290 assert json_response(conn, 200) == %{
1291 "expires_at" => nil,
1293 "invite_type" => "one_time",
1295 "token" => invite.token,
1301 test "with invalid token", %{conn: conn} do
1302 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1304 assert json_response(conn, :not_found) == "Not found"
1308 describe "GET /api/pleroma/admin/reports/:id" do
1309 test "returns report by its id", %{conn: conn} do
1310 [reporter, target_user] = insert_pair(:user)
1311 activity = insert(:note_activity, user: target_user)
1313 {:ok, %{id: report_id}} =
1314 CommonAPI.report(reporter, %{
1315 "account_id" => target_user.id,
1316 "comment" => "I feel offended",
1317 "status_ids" => [activity.id]
1322 |> get("/api/pleroma/admin/reports/#{report_id}")
1323 |> json_response(:ok)
1325 assert response["id"] == report_id
1328 test "returns 404 when report id is invalid", %{conn: conn} do
1329 conn = get(conn, "/api/pleroma/admin/reports/test")
1331 assert json_response(conn, :not_found) == "Not found"
1335 describe "PATCH /api/pleroma/admin/reports" do
1337 [reporter, target_user] = insert_pair(:user)
1338 activity = insert(:note_activity, user: target_user)
1340 {:ok, %{id: report_id}} =
1341 CommonAPI.report(reporter, %{
1342 "account_id" => target_user.id,
1343 "comment" => "I feel offended",
1344 "status_ids" => [activity.id]
1347 {:ok, %{id: second_report_id}} =
1348 CommonAPI.report(reporter, %{
1349 "account_id" => target_user.id,
1350 "comment" => "I feel very offended",
1351 "status_ids" => [activity.id]
1356 second_report_id: second_report_id
1360 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1361 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1362 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1366 |> assign(:token, read_token)
1367 |> patch("/api/pleroma/admin/reports", %{
1368 "reports" => [%{"state" => "resolved", "id" => id}]
1370 |> json_response(403)
1372 assert response == %{
1373 "error" => "Insufficient permissions: admin:write:reports."
1377 |> assign(:token, write_token)
1378 |> patch("/api/pleroma/admin/reports", %{
1379 "reports" => [%{"state" => "resolved", "id" => id}]
1381 |> json_response(:no_content)
1384 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1386 |> patch("/api/pleroma/admin/reports", %{
1388 %{"state" => "resolved", "id" => id}
1391 |> json_response(:no_content)
1393 activity = Activity.get_by_id(id)
1394 assert activity.data["state"] == "resolved"
1396 log_entry = Repo.one(ModerationLog)
1398 assert ModerationLog.get_log_entry_message(log_entry) ==
1399 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1402 test "closes report", %{conn: conn, id: id, admin: admin} do
1404 |> patch("/api/pleroma/admin/reports", %{
1406 %{"state" => "closed", "id" => id}
1409 |> json_response(:no_content)
1411 activity = Activity.get_by_id(id)
1412 assert activity.data["state"] == "closed"
1414 log_entry = Repo.one(ModerationLog)
1416 assert ModerationLog.get_log_entry_message(log_entry) ==
1417 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1420 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1423 |> patch("/api/pleroma/admin/reports", %{
1425 %{"state" => "test", "id" => id}
1429 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1432 test "returns 404 when report is not exist", %{conn: conn} do
1435 |> patch("/api/pleroma/admin/reports", %{
1437 %{"state" => "closed", "id" => "test"}
1441 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1444 test "updates state of multiple reports", %{
1448 second_report_id: second_report_id
1451 |> patch("/api/pleroma/admin/reports", %{
1453 %{"state" => "resolved", "id" => id},
1454 %{"state" => "closed", "id" => second_report_id}
1457 |> json_response(:no_content)
1459 activity = Activity.get_by_id(id)
1460 second_activity = Activity.get_by_id(second_report_id)
1461 assert activity.data["state"] == "resolved"
1462 assert second_activity.data["state"] == "closed"
1464 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1466 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1467 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1469 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1470 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1474 describe "GET /api/pleroma/admin/reports" do
1475 test "returns empty response when no reports created", %{conn: conn} do
1478 |> get("/api/pleroma/admin/reports")
1479 |> json_response(:ok)
1481 assert Enum.empty?(response["reports"])
1482 assert response["total"] == 0
1485 test "returns reports", %{conn: conn} do
1486 [reporter, target_user] = insert_pair(:user)
1487 activity = insert(:note_activity, user: target_user)
1489 {:ok, %{id: report_id}} =
1490 CommonAPI.report(reporter, %{
1491 "account_id" => target_user.id,
1492 "comment" => "I feel offended",
1493 "status_ids" => [activity.id]
1498 |> get("/api/pleroma/admin/reports")
1499 |> json_response(:ok)
1501 [report] = response["reports"]
1503 assert length(response["reports"]) == 1
1504 assert report["id"] == report_id
1506 assert response["total"] == 1
1509 test "returns reports with specified state", %{conn: conn} do
1510 [reporter, target_user] = insert_pair(:user)
1511 activity = insert(:note_activity, user: target_user)
1513 {:ok, %{id: first_report_id}} =
1514 CommonAPI.report(reporter, %{
1515 "account_id" => target_user.id,
1516 "comment" => "I feel offended",
1517 "status_ids" => [activity.id]
1520 {:ok, %{id: second_report_id}} =
1521 CommonAPI.report(reporter, %{
1522 "account_id" => target_user.id,
1523 "comment" => "I don't like this user"
1526 CommonAPI.update_report_state(second_report_id, "closed")
1530 |> get("/api/pleroma/admin/reports", %{
1533 |> json_response(:ok)
1535 [open_report] = response["reports"]
1537 assert length(response["reports"]) == 1
1538 assert open_report["id"] == first_report_id
1540 assert response["total"] == 1
1544 |> get("/api/pleroma/admin/reports", %{
1547 |> json_response(:ok)
1549 [closed_report] = response["reports"]
1551 assert length(response["reports"]) == 1
1552 assert closed_report["id"] == second_report_id
1554 assert response["total"] == 1
1558 |> get("/api/pleroma/admin/reports", %{
1559 "state" => "resolved"
1561 |> json_response(:ok)
1563 assert Enum.empty?(response["reports"])
1564 assert response["total"] == 0
1567 test "returns 403 when requested by a non-admin" do
1568 user = insert(:user)
1569 token = insert(:oauth_token, user: user)
1573 |> assign(:user, user)
1574 |> assign(:token, token)
1575 |> get("/api/pleroma/admin/reports")
1577 assert json_response(conn, :forbidden) ==
1578 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1581 test "returns 403 when requested by anonymous" do
1582 conn = get(build_conn(), "/api/pleroma/admin/reports")
1584 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1588 describe "PUT /api/pleroma/admin/statuses/:id" do
1590 activity = insert(:note_activity)
1595 test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
1598 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
1599 |> json_response(:ok)
1601 assert response["sensitive"]
1603 log_entry = Repo.one(ModerationLog)
1605 assert ModerationLog.get_log_entry_message(log_entry) ==
1606 "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
1610 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
1611 |> json_response(:ok)
1613 refute response["sensitive"]
1616 test "change visibility flag", %{conn: conn, id: id, admin: admin} do
1619 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
1620 |> json_response(:ok)
1622 assert response["visibility"] == "public"
1624 log_entry = Repo.one(ModerationLog)
1626 assert ModerationLog.get_log_entry_message(log_entry) ==
1627 "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
1631 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
1632 |> json_response(:ok)
1634 assert response["visibility"] == "private"
1638 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
1639 |> json_response(:ok)
1641 assert response["visibility"] == "unlisted"
1644 test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
1645 conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})
1647 assert json_response(conn, :bad_request) == "Unsupported visibility"
1651 describe "DELETE /api/pleroma/admin/statuses/:id" do
1653 activity = insert(:note_activity)
1658 test "deletes status", %{conn: conn, id: id, admin: admin} do
1660 |> delete("/api/pleroma/admin/statuses/#{id}")
1661 |> json_response(:ok)
1663 refute Activity.get_by_id(id)
1665 log_entry = Repo.one(ModerationLog)
1667 assert ModerationLog.get_log_entry_message(log_entry) ==
1668 "@#{admin.nickname} deleted status ##{id}"
1671 test "returns 404 when the status does not exist", %{conn: conn} do
1672 conn = delete(conn, "/api/pleroma/admin/statuses/test")
1674 assert json_response(conn, :not_found) == "Not found"
1678 describe "GET /api/pleroma/admin/config" do
1679 setup do: clear_config(:configurable_from_database, true)
1681 test "when configuration from database is off", %{conn: conn} do
1682 Config.put(:configurable_from_database, false)
1683 conn = get(conn, "/api/pleroma/admin/config")
1685 assert json_response(conn, 400) ==
1686 "To use this endpoint you need to enable configuration from database."
1689 test "with settings only in db", %{conn: conn} do
1690 config1 = insert(:config)
1691 config2 = insert(:config)
1693 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1698 "group" => ":pleroma",
1703 "group" => ":pleroma",
1708 } = json_response(conn, 200)
1710 assert key1 == config1.key
1711 assert key2 == config2.key
1714 test "db is added to settings that are in db", %{conn: conn} do
1715 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1717 %{"configs" => configs} =
1719 |> get("/api/pleroma/admin/config")
1720 |> json_response(200)
1723 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1724 group == ":pleroma" and key == ":instance"
1727 assert instance_config["db"] == [":name"]
1730 test "merged default setting with db settings", %{conn: conn} do
1731 config1 = insert(:config)
1732 config2 = insert(:config)
1736 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1739 %{"configs" => configs} =
1741 |> get("/api/pleroma/admin/config")
1742 |> json_response(200)
1744 assert length(configs) > 3
1747 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1748 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1751 assert length(received_configs) == 3
1755 |> ConfigDB.from_binary()
1757 |> ConfigDB.convert()
1759 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1760 assert db in [[config1.key], [config2.key], db_keys]
1763 ConfigDB.from_binary_with_convert(config1.value),
1764 ConfigDB.from_binary_with_convert(config2.value),
1765 ConfigDB.from_binary_with_convert(config3.value)
1770 test "subkeys with full update right merge", %{conn: conn} do
1774 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1780 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1783 %{"configs" => configs} =
1785 |> get("/api/pleroma/admin/config")
1786 |> json_response(200)
1789 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1790 group == ":pleroma" and key in [config1.key, config2.key]
1793 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
1794 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
1796 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
1797 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
1799 assert emoji_val[:groups] == [a: 1, b: 2]
1800 assert assets_val[:mascots] == [a: 1, b: 2]
1804 test "POST /api/pleroma/admin/config error", %{conn: conn} do
1805 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
1807 assert json_response(conn, 400) ==
1808 "To use this endpoint you need to enable configuration from database."
1811 describe "POST /api/pleroma/admin/config" do
1813 http = Application.get_env(:pleroma, :http)
1816 Application.delete_env(:pleroma, :key1)
1817 Application.delete_env(:pleroma, :key2)
1818 Application.delete_env(:pleroma, :key3)
1819 Application.delete_env(:pleroma, :key4)
1820 Application.delete_env(:pleroma, :keyaa1)
1821 Application.delete_env(:pleroma, :keyaa2)
1822 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1823 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1824 Application.put_env(:pleroma, :http, http)
1825 Application.put_env(:tesla, :adapter, Tesla.Mock)
1826 Restarter.Pleroma.refresh()
1830 setup do: clear_config(:configurable_from_database, true)
1832 @tag capture_log: true
1833 test "create new config setting in db", %{conn: conn} do
1834 ueberauth = Application.get_env(:ueberauth, Ueberauth)
1835 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
1838 post(conn, "/api/pleroma/admin/config", %{
1840 %{group: ":pleroma", key: ":key1", value: "value1"},
1842 group: ":ueberauth",
1844 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1850 ":nested_1" => "nested_value1",
1852 %{":nested_22" => "nested_value222"},
1853 %{":nested_33" => %{":nested_44" => "nested_444"}}
1861 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1862 %{"nested_4" => true}
1868 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
1873 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
1878 assert json_response(conn, 200) == %{
1881 "group" => ":pleroma",
1883 "value" => "value1",
1887 "group" => ":ueberauth",
1888 "key" => "Ueberauth",
1889 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
1890 "db" => [":consumer_secret"]
1893 "group" => ":pleroma",
1896 ":nested_1" => "nested_value1",
1898 %{":nested_22" => "nested_value222"},
1899 %{":nested_33" => %{":nested_44" => "nested_444"}}
1905 "group" => ":pleroma",
1908 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1909 %{"nested_4" => true}
1914 "group" => ":pleroma",
1916 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
1922 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
1928 assert Application.get_env(:pleroma, :key1) == "value1"
1930 assert Application.get_env(:pleroma, :key2) == %{
1931 nested_1: "nested_value1",
1933 %{nested_22: "nested_value222"},
1934 %{nested_33: %{nested_44: "nested_444"}}
1938 assert Application.get_env(:pleroma, :key3) == [
1939 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
1940 %{"nested_4" => true}
1943 assert Application.get_env(:pleroma, :key4) == %{
1944 "endpoint" => "https://example.com",
1948 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
1951 test "save configs setting without explicit key", %{conn: conn} do
1952 level = Application.get_env(:quack, :level)
1953 meta = Application.get_env(:quack, :meta)
1954 webhook_url = Application.get_env(:quack, :webhook_url)
1957 Application.put_env(:quack, :level, level)
1958 Application.put_env(:quack, :meta, meta)
1959 Application.put_env(:quack, :webhook_url, webhook_url)
1963 post(conn, "/api/pleroma/admin/config", %{
1977 key: ":webhook_url",
1978 value: "https://hooks.slack.com/services/KEY"
1983 assert json_response(conn, 200) == %{
1986 "group" => ":quack",
1992 "group" => ":quack",
1994 "value" => [":none"],
1998 "group" => ":quack",
1999 "key" => ":webhook_url",
2000 "value" => "https://hooks.slack.com/services/KEY",
2001 "db" => [":webhook_url"]
2006 assert Application.get_env(:quack, :level) == :info
2007 assert Application.get_env(:quack, :meta) == [:none]
2008 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
2011 test "saving config with partial update", %{conn: conn} do
2012 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
2015 post(conn, "/api/pleroma/admin/config", %{
2017 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
2021 assert json_response(conn, 200) == %{
2024 "group" => ":pleroma",
2027 %{"tuple" => [":key1", 1]},
2028 %{"tuple" => [":key2", 2]},
2029 %{"tuple" => [":key3", 3]}
2031 "db" => [":key1", ":key2", ":key3"]
2037 test "saving config which need pleroma reboot", %{conn: conn} do
2038 chat = Config.get(:chat)
2039 on_exit(fn -> Config.put(:chat, chat) end)
2043 "/api/pleroma/admin/config",
2046 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2050 |> json_response(200) == %{
2053 "db" => [":enabled"],
2054 "group" => ":pleroma",
2056 "value" => [%{"tuple" => [":enabled", true]}]
2059 "need_reboot" => true
2064 |> get("/api/pleroma/admin/config")
2065 |> json_response(200)
2067 assert configs["need_reboot"]
2070 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2071 end) =~ "pleroma restarted"
2075 |> get("/api/pleroma/admin/config")
2076 |> json_response(200)
2078 refute Map.has_key?(configs, "need_reboot")
2081 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
2082 chat = Config.get(:chat)
2083 on_exit(fn -> Config.put(:chat, chat) end)
2087 "/api/pleroma/admin/config",
2090 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2094 |> json_response(200) == %{
2097 "db" => [":enabled"],
2098 "group" => ":pleroma",
2100 "value" => [%{"tuple" => [":enabled", true]}]
2103 "need_reboot" => true
2106 assert post(conn, "/api/pleroma/admin/config", %{
2108 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
2111 |> json_response(200) == %{
2114 "group" => ":pleroma",
2117 %{"tuple" => [":key3", 3]}
2122 "need_reboot" => true
2126 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2127 end) =~ "pleroma restarted"
2131 |> get("/api/pleroma/admin/config")
2132 |> json_response(200)
2134 refute Map.has_key?(configs, "need_reboot")
2137 test "saving config with nested merge", %{conn: conn} do
2139 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
2142 post(conn, "/api/pleroma/admin/config", %{
2145 group: config.group,
2148 %{"tuple" => [":key3", 3]},
2153 %{"tuple" => [":k2", 1]},
2154 %{"tuple" => [":k3", 3]}
2163 assert json_response(conn, 200) == %{
2166 "group" => ":pleroma",
2169 %{"tuple" => [":key1", 1]},
2170 %{"tuple" => [":key3", 3]},
2175 %{"tuple" => [":k1", 1]},
2176 %{"tuple" => [":k2", 1]},
2177 %{"tuple" => [":k3", 3]}
2182 "db" => [":key1", ":key3", ":key2"]
2188 test "saving special atoms", %{conn: conn} do
2190 post(conn, "/api/pleroma/admin/config", %{
2193 "group" => ":pleroma",
2199 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2207 assert json_response(conn, 200) == %{
2210 "group" => ":pleroma",
2216 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2220 "db" => [":ssl_options"]
2225 assert Application.get_env(:pleroma, :key1) == [
2226 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
2230 test "saving full setting if value is in full_key_update list", %{conn: conn} do
2231 backends = Application.get_env(:logger, :backends)
2232 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
2238 value: :erlang.term_to_binary([])
2242 post(conn, "/api/pleroma/admin/config", %{
2245 group: config.group,
2247 value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}]
2252 assert json_response(conn, 200) == %{
2255 "group" => ":logger",
2256 "key" => ":backends",
2259 %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}
2261 "db" => [":backends"]
2266 assert Application.get_env(:logger, :backends) == [
2268 {ExSyslogger, :ex_syslogger}
2273 Logger.warn("Ooops...")
2277 test "saving full setting if value is not keyword", %{conn: conn} do
2282 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
2286 post(conn, "/api/pleroma/admin/config", %{
2288 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
2292 assert json_response(conn, 200) == %{
2295 "group" => ":tesla",
2296 "key" => ":adapter",
2297 "value" => "Tesla.Adapter.Httpc",
2298 "db" => [":adapter"]
2304 test "update config setting & delete with fallback to default value", %{
2309 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2310 config1 = insert(:config, key: ":keyaa1")
2311 config2 = insert(:config, key: ":keyaa2")
2315 group: ":ueberauth",
2320 post(conn, "/api/pleroma/admin/config", %{
2322 %{group: config1.group, key: config1.key, value: "another_value"},
2323 %{group: config2.group, key: config2.key, value: "another_value"}
2327 assert json_response(conn, 200) == %{
2330 "group" => ":pleroma",
2331 "key" => config1.key,
2332 "value" => "another_value",
2336 "group" => ":pleroma",
2337 "key" => config2.key,
2338 "value" => "another_value",
2344 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2345 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
2346 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
2350 |> assign(:user, admin)
2351 |> assign(:token, token)
2352 |> post("/api/pleroma/admin/config", %{
2354 %{group: config2.group, key: config2.key, delete: true},
2356 group: ":ueberauth",
2363 assert json_response(conn, 200) == %{
2367 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
2368 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
2371 test "common config example", %{conn: conn} do
2373 post(conn, "/api/pleroma/admin/config", %{
2376 "group" => ":pleroma",
2377 "key" => "Pleroma.Captcha.NotReal",
2379 %{"tuple" => [":enabled", false]},
2380 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2381 %{"tuple" => [":seconds_valid", 60]},
2382 %{"tuple" => [":path", ""]},
2383 %{"tuple" => [":key1", nil]},
2384 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2385 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2386 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2387 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2388 %{"tuple" => [":name", "Pleroma"]}
2394 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2396 assert json_response(conn, 200) == %{
2399 "group" => ":pleroma",
2400 "key" => "Pleroma.Captcha.NotReal",
2402 %{"tuple" => [":enabled", false]},
2403 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2404 %{"tuple" => [":seconds_valid", 60]},
2405 %{"tuple" => [":path", ""]},
2406 %{"tuple" => [":key1", nil]},
2407 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2408 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2409 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2410 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2411 %{"tuple" => [":name", "Pleroma"]}
2430 test "tuples with more than two values", %{conn: conn} do
2432 post(conn, "/api/pleroma/admin/config", %{
2435 "group" => ":pleroma",
2436 "key" => "Pleroma.Web.Endpoint.NotReal",
2452 "/api/v1/streaming",
2453 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2460 "Phoenix.Endpoint.CowboyWebSocket",
2463 "Phoenix.Transports.WebSocket",
2466 "Pleroma.Web.Endpoint",
2467 "Pleroma.Web.UserSocket",
2478 "Phoenix.Endpoint.Cowboy2Handler",
2479 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2496 assert json_response(conn, 200) == %{
2499 "group" => ":pleroma",
2500 "key" => "Pleroma.Web.Endpoint.NotReal",
2516 "/api/v1/streaming",
2517 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2524 "Phoenix.Endpoint.CowboyWebSocket",
2527 "Phoenix.Transports.WebSocket",
2530 "Pleroma.Web.Endpoint",
2531 "Pleroma.Web.UserSocket",
2542 "Phoenix.Endpoint.Cowboy2Handler",
2543 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2562 test "settings with nesting map", %{conn: conn} do
2564 post(conn, "/api/pleroma/admin/config", %{
2567 "group" => ":pleroma",
2570 %{"tuple" => [":key2", "some_val"]},
2575 ":max_options" => 20,
2576 ":max_option_chars" => 200,
2577 ":min_expiration" => 0,
2578 ":max_expiration" => 31_536_000,
2580 ":max_options" => 20,
2581 ":max_option_chars" => 200,
2582 ":min_expiration" => 0,
2583 ":max_expiration" => 31_536_000
2593 assert json_response(conn, 200) ==
2597 "group" => ":pleroma",
2600 %{"tuple" => [":key2", "some_val"]},
2605 ":max_expiration" => 31_536_000,
2606 ":max_option_chars" => 200,
2607 ":max_options" => 20,
2608 ":min_expiration" => 0,
2610 ":max_expiration" => 31_536_000,
2611 ":max_option_chars" => 200,
2612 ":max_options" => 20,
2613 ":min_expiration" => 0
2619 "db" => [":key2", ":key3"]
2625 test "value as map", %{conn: conn} do
2627 post(conn, "/api/pleroma/admin/config", %{
2630 "group" => ":pleroma",
2632 "value" => %{"key" => "some_val"}
2637 assert json_response(conn, 200) ==
2641 "group" => ":pleroma",
2643 "value" => %{"key" => "some_val"},
2650 test "queues key as atom", %{conn: conn} do
2652 post(conn, "/api/pleroma/admin/config", %{
2658 %{"tuple" => [":federator_incoming", 50]},
2659 %{"tuple" => [":federator_outgoing", 50]},
2660 %{"tuple" => [":web_push", 50]},
2661 %{"tuple" => [":mailer", 10]},
2662 %{"tuple" => [":transmogrifier", 20]},
2663 %{"tuple" => [":scheduled_activities", 10]},
2664 %{"tuple" => [":background", 5]}
2670 assert json_response(conn, 200) == %{
2676 %{"tuple" => [":federator_incoming", 50]},
2677 %{"tuple" => [":federator_outgoing", 50]},
2678 %{"tuple" => [":web_push", 50]},
2679 %{"tuple" => [":mailer", 10]},
2680 %{"tuple" => [":transmogrifier", 20]},
2681 %{"tuple" => [":scheduled_activities", 10]},
2682 %{"tuple" => [":background", 5]}
2685 ":federator_incoming",
2686 ":federator_outgoing",
2690 ":scheduled_activities",
2698 test "delete part of settings by atom subkeys", %{conn: conn} do
2702 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2706 post(conn, "/api/pleroma/admin/config", %{
2709 group: config.group,
2711 subkeys: [":subkey1", ":subkey3"],
2717 assert json_response(conn, 200) == %{
2720 "group" => ":pleroma",
2722 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2723 "db" => [":subkey2"]
2729 test "proxy tuple localhost", %{conn: conn} do
2731 post(conn, "/api/pleroma/admin/config", %{
2737 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2738 %{"tuple" => [":send_user_agent", false]}
2744 assert json_response(conn, 200) == %{
2747 "group" => ":pleroma",
2750 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2751 %{"tuple" => [":send_user_agent", false]}
2753 "db" => [":proxy_url", ":send_user_agent"]
2759 test "proxy tuple domain", %{conn: conn} do
2761 post(conn, "/api/pleroma/admin/config", %{
2767 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
2768 %{"tuple" => [":send_user_agent", false]}
2774 assert json_response(conn, 200) == %{
2777 "group" => ":pleroma",
2780 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
2781 %{"tuple" => [":send_user_agent", false]}
2783 "db" => [":proxy_url", ":send_user_agent"]
2789 test "proxy tuple ip", %{conn: conn} do
2791 post(conn, "/api/pleroma/admin/config", %{
2797 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
2798 %{"tuple" => [":send_user_agent", false]}
2804 assert json_response(conn, 200) == %{
2807 "group" => ":pleroma",
2810 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
2811 %{"tuple" => [":send_user_agent", false]}
2813 "db" => [":proxy_url", ":send_user_agent"]
2820 describe "GET /api/pleroma/admin/restart" do
2821 setup do: clear_config(:configurable_from_database, true)
2823 test "pleroma restarts", %{conn: conn} do
2825 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2826 end) =~ "pleroma restarted"
2828 refute Restarter.Pleroma.need_reboot?()
2832 describe "GET /api/pleroma/admin/statuses" do
2833 test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
2834 blocked = insert(:user)
2835 user = insert(:user)
2836 User.block(admin, blocked)
2839 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
2841 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
2842 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
2843 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
2844 {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
2848 |> get("/api/pleroma/admin/statuses")
2849 |> json_response(200)
2851 refute "private" in Enum.map(response, & &1["visibility"])
2852 assert length(response) == 3
2855 test "returns only local statuses with local_only on", %{conn: conn} do
2856 user = insert(:user)
2857 remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
2858 insert(:note_activity, user: user, local: true)
2859 insert(:note_activity, user: remote_user, local: false)
2863 |> get("/api/pleroma/admin/statuses?local_only=true")
2864 |> json_response(200)
2866 assert length(response) == 1
2869 test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
2870 user = insert(:user)
2873 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
2875 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
2876 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
2877 conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
2878 assert json_response(conn, 200) |> length() == 3
2882 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2884 user = insert(:user)
2886 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2887 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2888 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2890 insert(:note_activity, user: user, published: date1)
2891 insert(:note_activity, user: user, published: date2)
2892 insert(:note_activity, user: user, published: date3)
2897 test "renders user's statuses", %{conn: conn, user: user} do
2898 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2900 assert json_response(conn, 200) |> length() == 3
2903 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2904 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2906 assert json_response(conn, 200) |> length() == 2
2909 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2910 {:ok, _private_status} =
2911 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2913 {:ok, _public_status} =
2914 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2916 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2918 assert json_response(conn, 200) |> length() == 4
2921 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2922 {:ok, _private_status} =
2923 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2925 {:ok, _public_status} =
2926 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2928 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2930 assert json_response(conn, 200) |> length() == 5
2933 test "excludes reblogs by default", %{conn: conn, user: user} do
2934 other_user = insert(:user)
2935 {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
2936 {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
2938 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
2939 assert json_response(conn_res, 200) |> length() == 0
2942 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
2944 assert json_response(conn_res, 200) |> length() == 1
2948 describe "GET /api/pleroma/admin/moderation_log" do
2950 moderator = insert(:user, is_moderator: true)
2952 %{moderator: moderator}
2955 test "returns the log", %{conn: conn, admin: admin} do
2956 Repo.insert(%ModerationLog{
2960 "nickname" => admin.nickname,
2963 action: "relay_follow",
2964 target: "https://example.org/relay"
2966 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2969 Repo.insert(%ModerationLog{
2973 "nickname" => admin.nickname,
2976 action: "relay_unfollow",
2977 target: "https://example.org/relay"
2979 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2982 conn = get(conn, "/api/pleroma/admin/moderation_log")
2984 response = json_response(conn, 200)
2985 [first_entry, second_entry] = response["items"]
2987 assert response["total"] == 2
2988 assert first_entry["data"]["action"] == "relay_unfollow"
2990 assert first_entry["message"] ==
2991 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2993 assert second_entry["data"]["action"] == "relay_follow"
2995 assert second_entry["message"] ==
2996 "@#{admin.nickname} followed relay: https://example.org/relay"
2999 test "returns the log with pagination", %{conn: conn, admin: admin} do
3000 Repo.insert(%ModerationLog{
3004 "nickname" => admin.nickname,
3007 action: "relay_follow",
3008 target: "https://example.org/relay"
3010 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3013 Repo.insert(%ModerationLog{
3017 "nickname" => admin.nickname,
3020 action: "relay_unfollow",
3021 target: "https://example.org/relay"
3023 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3026 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3028 response1 = json_response(conn1, 200)
3029 [first_entry] = response1["items"]
3031 assert response1["total"] == 2
3032 assert response1["items"] |> length() == 1
3033 assert first_entry["data"]["action"] == "relay_unfollow"
3035 assert first_entry["message"] ==
3036 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3038 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3040 response2 = json_response(conn2, 200)
3041 [second_entry] = response2["items"]
3043 assert response2["total"] == 2
3044 assert response2["items"] |> length() == 1
3045 assert second_entry["data"]["action"] == "relay_follow"
3047 assert second_entry["message"] ==
3048 "@#{admin.nickname} followed relay: https://example.org/relay"
3051 test "filters log by date", %{conn: conn, admin: admin} do
3052 first_date = "2017-08-15T15:47:06Z"
3053 second_date = "2017-08-20T15:47:06Z"
3055 Repo.insert(%ModerationLog{
3059 "nickname" => admin.nickname,
3062 action: "relay_follow",
3063 target: "https://example.org/relay"
3065 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3068 Repo.insert(%ModerationLog{
3072 "nickname" => admin.nickname,
3075 action: "relay_unfollow",
3076 target: "https://example.org/relay"
3078 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3084 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3087 response1 = json_response(conn1, 200)
3088 [first_entry] = response1["items"]
3090 assert response1["total"] == 1
3091 assert first_entry["data"]["action"] == "relay_unfollow"
3093 assert first_entry["message"] ==
3094 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3097 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3098 Repo.insert(%ModerationLog{
3102 "nickname" => admin.nickname,
3105 action: "relay_follow",
3106 target: "https://example.org/relay"
3110 Repo.insert(%ModerationLog{
3113 "id" => moderator.id,
3114 "nickname" => moderator.nickname,
3117 action: "relay_unfollow",
3118 target: "https://example.org/relay"
3122 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3124 response1 = json_response(conn1, 200)
3125 [first_entry] = response1["items"]
3127 assert response1["total"] == 1
3128 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3131 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3132 ModerationLog.insert_log(%{
3134 action: "relay_follow",
3135 target: "https://example.org/relay"
3138 ModerationLog.insert_log(%{
3140 action: "relay_unfollow",
3141 target: "https://example.org/relay"
3144 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3146 response1 = json_response(conn1, 200)
3147 [first_entry] = response1["items"]
3149 assert response1["total"] == 1
3151 assert get_in(first_entry, ["data", "message"]) ==
3152 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3156 describe "GET /users/:nickname/credentials" do
3157 test "gets the user credentials", %{conn: conn} do
3158 user = insert(:user)
3159 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3161 response = assert json_response(conn, 200)
3162 assert response["email"] == user.email
3165 test "returns 403 if requested by a non-admin" do
3166 user = insert(:user)
3170 |> assign(:user, user)
3171 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3173 assert json_response(conn, :forbidden)
3177 describe "PATCH /users/:nickname/credentials" do
3178 test "changes password and email", %{conn: conn, admin: admin} do
3179 user = insert(:user)
3180 assert user.password_reset_pending == false
3183 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3184 "password" => "new_password",
3185 "email" => "new_email@example.com",
3186 "name" => "new_name"
3189 assert json_response(conn, 200) == %{"status" => "success"}
3191 ObanHelpers.perform_all()
3193 updated_user = User.get_by_id(user.id)
3195 assert updated_user.email == "new_email@example.com"
3196 assert updated_user.name == "new_name"
3197 assert updated_user.password_hash != user.password_hash
3198 assert updated_user.password_reset_pending == true
3200 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3202 assert ModerationLog.get_log_entry_message(log_entry1) ==
3203 "@#{admin.nickname} updated users: @#{user.nickname}"
3205 assert ModerationLog.get_log_entry_message(log_entry2) ==
3206 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3209 test "returns 403 if requested by a non-admin" do
3210 user = insert(:user)
3214 |> assign(:user, user)
3215 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3216 "password" => "new_password",
3217 "email" => "new_email@example.com",
3218 "name" => "new_name"
3221 assert json_response(conn, :forbidden)
3225 describe "PATCH /users/:nickname/force_password_reset" do
3226 test "sets password_reset_pending to true", %{conn: conn} do
3227 user = insert(:user)
3228 assert user.password_reset_pending == false
3231 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3233 assert json_response(conn, 204) == ""
3235 ObanHelpers.perform_all()
3237 assert User.get_by_id(user.id).password_reset_pending == true
3241 describe "relays" do
3242 test "POST /relay", %{conn: conn, admin: admin} do
3244 post(conn, "/api/pleroma/admin/relay", %{
3245 relay_url: "http://mastodon.example.org/users/admin"
3248 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3250 log_entry = Repo.one(ModerationLog)
3252 assert ModerationLog.get_log_entry_message(log_entry) ==
3253 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3256 test "GET /relay", %{conn: conn} do
3257 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3259 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3260 |> Enum.each(fn ap_id ->
3261 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3262 User.follow(relay_user, user)
3265 conn = get(conn, "/api/pleroma/admin/relay")
3267 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3270 test "DELETE /relay", %{conn: conn, admin: admin} do
3271 post(conn, "/api/pleroma/admin/relay", %{
3272 relay_url: "http://mastodon.example.org/users/admin"
3276 delete(conn, "/api/pleroma/admin/relay", %{
3277 relay_url: "http://mastodon.example.org/users/admin"
3280 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3282 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3284 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3285 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3287 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3288 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3292 describe "instances" do
3293 test "GET /instances/:instance/statuses", %{conn: conn} do
3294 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3295 user2 = insert(:user, local: false, nickname: "test@test.com")
3296 insert_pair(:note_activity, user: user)
3297 activity = insert(:note_activity, user: user2)
3299 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3301 response = json_response(ret_conn, 200)
3303 assert length(response) == 2
3305 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3307 response = json_response(ret_conn, 200)
3309 assert length(response) == 1
3311 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3313 response = json_response(ret_conn, 200)
3315 assert Enum.empty?(response)
3317 CommonAPI.repeat(activity.id, user)
3319 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3320 response = json_response(ret_conn, 200)
3321 assert length(response) == 2
3323 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3324 response = json_response(ret_conn, 200)
3325 assert length(response) == 3
3329 describe "PATCH /confirm_email" do
3330 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3331 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3333 assert first_user.confirmation_pending == true
3334 assert second_user.confirmation_pending == true
3337 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3339 first_user.nickname,
3340 second_user.nickname
3344 assert ret_conn.status == 200
3346 assert first_user.confirmation_pending == true
3347 assert second_user.confirmation_pending == true
3349 log_entry = Repo.one(ModerationLog)
3351 assert ModerationLog.get_log_entry_message(log_entry) ==
3352 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3353 second_user.nickname
3358 describe "PATCH /resend_confirmation_email" do
3359 test "it resend emails for two users", %{conn: conn, admin: admin} do
3360 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3363 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3365 first_user.nickname,
3366 second_user.nickname
3370 assert ret_conn.status == 200
3372 log_entry = Repo.one(ModerationLog)
3374 assert ModerationLog.get_log_entry_message(log_entry) ==
3375 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3376 second_user.nickname
3381 describe "POST /reports/:id/notes" do
3382 setup %{conn: conn, admin: admin} do
3383 [reporter, target_user] = insert_pair(:user)
3384 activity = insert(:note_activity, user: target_user)
3386 {:ok, %{id: report_id}} =
3387 CommonAPI.report(reporter, %{
3388 "account_id" => target_user.id,
3389 "comment" => "I feel offended",
3390 "status_ids" => [activity.id]
3393 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3394 content: "this is disgusting!"
3397 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3398 content: "this is disgusting2!"
3403 report_id: report_id
3407 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3408 [note, _] = Repo.all(ReportNote)
3411 activity_id: ^report_id,
3412 content: "this is disgusting!",
3417 test "it returns reports with notes", %{conn: conn, admin: admin} do
3418 conn = get(conn, "/api/pleroma/admin/reports")
3420 response = json_response(conn, 200)
3421 notes = hd(response["reports"])["notes"]
3424 assert note["user"]["nickname"] == admin.nickname
3425 assert note["content"] == "this is disgusting!"
3426 assert note["created_at"]
3427 assert response["total"] == 1
3430 test "it deletes the note", %{conn: conn, report_id: report_id} do
3431 assert ReportNote |> Repo.all() |> length() == 2
3433 [note, _] = Repo.all(ReportNote)
3435 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3437 assert ReportNote |> Repo.all() |> length() == 1
3441 test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do
3442 admin = insert(:user, is_admin: true)
3445 assign(conn, :user, admin)
3446 |> get("/api/pleroma/admin/config/descriptions")
3448 assert [child | _others] = json_response(conn, 200)
3450 assert child["children"]
3452 assert String.starts_with?(child["group"], ":")
3453 assert child["description"]
3456 describe "/api/pleroma/admin/stats" do
3457 test "status visibility count", %{conn: conn} do
3458 admin = insert(:user, is_admin: true)
3459 user = insert(:user)
3460 CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
3461 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3462 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3466 |> assign(:user, admin)
3467 |> get("/api/pleroma/admin/stats")
3468 |> json_response(200)
3470 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3471 response["status_visibility"]
3476 # Needed for testing
3477 defmodule Pleroma.Web.Endpoint.NotReal do
3480 defmodule Pleroma.Captcha.NotReal do