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
2372 adapter = Application.get_env(:tesla, :adapter)
2373 on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
2376 post(conn, "/api/pleroma/admin/config", %{
2379 "group" => ":pleroma",
2380 "key" => "Pleroma.Captcha.NotReal",
2382 %{"tuple" => [":enabled", false]},
2383 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2384 %{"tuple" => [":seconds_valid", 60]},
2385 %{"tuple" => [":path", ""]},
2386 %{"tuple" => [":key1", nil]},
2387 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2388 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2389 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2390 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2391 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2392 %{"tuple" => [":name", "Pleroma"]}
2396 "group" => ":tesla",
2397 "key" => ":adapter",
2398 "value" => "Tesla.Adapter.Httpc"
2403 assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
2404 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2406 assert json_response(conn, 200) == %{
2409 "group" => ":pleroma",
2410 "key" => "Pleroma.Captcha.NotReal",
2412 %{"tuple" => [":enabled", false]},
2413 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2414 %{"tuple" => [":seconds_valid", 60]},
2415 %{"tuple" => [":path", ""]},
2416 %{"tuple" => [":key1", nil]},
2417 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2418 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2419 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2420 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2421 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2422 %{"tuple" => [":name", "Pleroma"]}
2439 "group" => ":tesla",
2440 "key" => ":adapter",
2441 "value" => "Tesla.Adapter.Httpc",
2442 "db" => [":adapter"]
2448 test "tuples with more than two values", %{conn: conn} do
2450 post(conn, "/api/pleroma/admin/config", %{
2453 "group" => ":pleroma",
2454 "key" => "Pleroma.Web.Endpoint.NotReal",
2470 "/api/v1/streaming",
2471 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2478 "Phoenix.Endpoint.CowboyWebSocket",
2481 "Phoenix.Transports.WebSocket",
2484 "Pleroma.Web.Endpoint",
2485 "Pleroma.Web.UserSocket",
2496 "Phoenix.Endpoint.Cowboy2Handler",
2497 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2514 assert json_response(conn, 200) == %{
2517 "group" => ":pleroma",
2518 "key" => "Pleroma.Web.Endpoint.NotReal",
2534 "/api/v1/streaming",
2535 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2542 "Phoenix.Endpoint.CowboyWebSocket",
2545 "Phoenix.Transports.WebSocket",
2548 "Pleroma.Web.Endpoint",
2549 "Pleroma.Web.UserSocket",
2560 "Phoenix.Endpoint.Cowboy2Handler",
2561 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2580 test "settings with nesting map", %{conn: conn} do
2582 post(conn, "/api/pleroma/admin/config", %{
2585 "group" => ":pleroma",
2588 %{"tuple" => [":key2", "some_val"]},
2593 ":max_options" => 20,
2594 ":max_option_chars" => 200,
2595 ":min_expiration" => 0,
2596 ":max_expiration" => 31_536_000,
2598 ":max_options" => 20,
2599 ":max_option_chars" => 200,
2600 ":min_expiration" => 0,
2601 ":max_expiration" => 31_536_000
2611 assert json_response(conn, 200) ==
2615 "group" => ":pleroma",
2618 %{"tuple" => [":key2", "some_val"]},
2623 ":max_expiration" => 31_536_000,
2624 ":max_option_chars" => 200,
2625 ":max_options" => 20,
2626 ":min_expiration" => 0,
2628 ":max_expiration" => 31_536_000,
2629 ":max_option_chars" => 200,
2630 ":max_options" => 20,
2631 ":min_expiration" => 0
2637 "db" => [":key2", ":key3"]
2643 test "value as map", %{conn: conn} do
2645 post(conn, "/api/pleroma/admin/config", %{
2648 "group" => ":pleroma",
2650 "value" => %{"key" => "some_val"}
2655 assert json_response(conn, 200) ==
2659 "group" => ":pleroma",
2661 "value" => %{"key" => "some_val"},
2668 test "queues key as atom", %{conn: conn} do
2670 post(conn, "/api/pleroma/admin/config", %{
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]}
2688 assert json_response(conn, 200) == %{
2694 %{"tuple" => [":federator_incoming", 50]},
2695 %{"tuple" => [":federator_outgoing", 50]},
2696 %{"tuple" => [":web_push", 50]},
2697 %{"tuple" => [":mailer", 10]},
2698 %{"tuple" => [":transmogrifier", 20]},
2699 %{"tuple" => [":scheduled_activities", 10]},
2700 %{"tuple" => [":background", 5]}
2703 ":federator_incoming",
2704 ":federator_outgoing",
2708 ":scheduled_activities",
2716 test "delete part of settings by atom subkeys", %{conn: conn} do
2720 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2724 post(conn, "/api/pleroma/admin/config", %{
2727 group: config.group,
2729 subkeys: [":subkey1", ":subkey3"],
2735 assert json_response(conn, 200) == %{
2738 "group" => ":pleroma",
2740 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2741 "db" => [":subkey2"]
2747 test "proxy tuple localhost", %{conn: conn} do
2749 post(conn, "/api/pleroma/admin/config", %{
2755 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2756 %{"tuple" => [":send_user_agent", false]}
2762 assert json_response(conn, 200) == %{
2765 "group" => ":pleroma",
2768 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2769 %{"tuple" => [":send_user_agent", false]}
2771 "db" => [":proxy_url", ":send_user_agent"]
2777 test "proxy tuple domain", %{conn: conn} do
2779 post(conn, "/api/pleroma/admin/config", %{
2785 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
2786 %{"tuple" => [":send_user_agent", false]}
2792 assert json_response(conn, 200) == %{
2795 "group" => ":pleroma",
2798 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
2799 %{"tuple" => [":send_user_agent", false]}
2801 "db" => [":proxy_url", ":send_user_agent"]
2807 test "proxy tuple ip", %{conn: conn} do
2809 post(conn, "/api/pleroma/admin/config", %{
2815 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
2816 %{"tuple" => [":send_user_agent", false]}
2822 assert json_response(conn, 200) == %{
2825 "group" => ":pleroma",
2828 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
2829 %{"tuple" => [":send_user_agent", false]}
2831 "db" => [":proxy_url", ":send_user_agent"]
2838 describe "GET /api/pleroma/admin/restart" do
2839 setup do: clear_config(:configurable_from_database, true)
2841 test "pleroma restarts", %{conn: conn} do
2843 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2844 end) =~ "pleroma restarted"
2846 refute Restarter.Pleroma.need_reboot?()
2850 describe "GET /api/pleroma/admin/statuses" do
2851 test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
2852 blocked = insert(:user)
2853 user = insert(:user)
2854 User.block(admin, blocked)
2857 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
2859 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
2860 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
2861 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
2862 {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
2866 |> get("/api/pleroma/admin/statuses")
2867 |> json_response(200)
2869 refute "private" in Enum.map(response, & &1["visibility"])
2870 assert length(response) == 3
2873 test "returns only local statuses with local_only on", %{conn: conn} do
2874 user = insert(:user)
2875 remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
2876 insert(:note_activity, user: user, local: true)
2877 insert(:note_activity, user: remote_user, local: false)
2881 |> get("/api/pleroma/admin/statuses?local_only=true")
2882 |> json_response(200)
2884 assert length(response) == 1
2887 test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
2888 user = insert(:user)
2891 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
2893 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
2894 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
2895 conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
2896 assert json_response(conn, 200) |> length() == 3
2900 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2902 user = insert(:user)
2904 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2905 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2906 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2908 insert(:note_activity, user: user, published: date1)
2909 insert(:note_activity, user: user, published: date2)
2910 insert(:note_activity, user: user, published: date3)
2915 test "renders user's statuses", %{conn: conn, user: user} do
2916 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2918 assert json_response(conn, 200) |> length() == 3
2921 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2922 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2924 assert json_response(conn, 200) |> length() == 2
2927 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2928 {:ok, _private_status} =
2929 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2931 {:ok, _public_status} =
2932 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2934 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2936 assert json_response(conn, 200) |> length() == 4
2939 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2940 {:ok, _private_status} =
2941 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2943 {:ok, _public_status} =
2944 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2946 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2948 assert json_response(conn, 200) |> length() == 5
2951 test "excludes reblogs by default", %{conn: conn, user: user} do
2952 other_user = insert(:user)
2953 {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
2954 {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
2956 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
2957 assert json_response(conn_res, 200) |> length() == 0
2960 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
2962 assert json_response(conn_res, 200) |> length() == 1
2966 describe "GET /api/pleroma/admin/moderation_log" do
2968 moderator = insert(:user, is_moderator: true)
2970 %{moderator: moderator}
2973 test "returns the log", %{conn: conn, admin: admin} do
2974 Repo.insert(%ModerationLog{
2978 "nickname" => admin.nickname,
2981 action: "relay_follow",
2982 target: "https://example.org/relay"
2984 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2987 Repo.insert(%ModerationLog{
2991 "nickname" => admin.nickname,
2994 action: "relay_unfollow",
2995 target: "https://example.org/relay"
2997 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3000 conn = get(conn, "/api/pleroma/admin/moderation_log")
3002 response = json_response(conn, 200)
3003 [first_entry, second_entry] = response["items"]
3005 assert response["total"] == 2
3006 assert first_entry["data"]["action"] == "relay_unfollow"
3008 assert first_entry["message"] ==
3009 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3011 assert second_entry["data"]["action"] == "relay_follow"
3013 assert second_entry["message"] ==
3014 "@#{admin.nickname} followed relay: https://example.org/relay"
3017 test "returns the log with pagination", %{conn: conn, admin: admin} do
3018 Repo.insert(%ModerationLog{
3022 "nickname" => admin.nickname,
3025 action: "relay_follow",
3026 target: "https://example.org/relay"
3028 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3031 Repo.insert(%ModerationLog{
3035 "nickname" => admin.nickname,
3038 action: "relay_unfollow",
3039 target: "https://example.org/relay"
3041 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3044 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3046 response1 = json_response(conn1, 200)
3047 [first_entry] = response1["items"]
3049 assert response1["total"] == 2
3050 assert response1["items"] |> length() == 1
3051 assert first_entry["data"]["action"] == "relay_unfollow"
3053 assert first_entry["message"] ==
3054 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3056 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3058 response2 = json_response(conn2, 200)
3059 [second_entry] = response2["items"]
3061 assert response2["total"] == 2
3062 assert response2["items"] |> length() == 1
3063 assert second_entry["data"]["action"] == "relay_follow"
3065 assert second_entry["message"] ==
3066 "@#{admin.nickname} followed relay: https://example.org/relay"
3069 test "filters log by date", %{conn: conn, admin: admin} do
3070 first_date = "2017-08-15T15:47:06Z"
3071 second_date = "2017-08-20T15:47:06Z"
3073 Repo.insert(%ModerationLog{
3077 "nickname" => admin.nickname,
3080 action: "relay_follow",
3081 target: "https://example.org/relay"
3083 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3086 Repo.insert(%ModerationLog{
3090 "nickname" => admin.nickname,
3093 action: "relay_unfollow",
3094 target: "https://example.org/relay"
3096 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3102 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3105 response1 = json_response(conn1, 200)
3106 [first_entry] = response1["items"]
3108 assert response1["total"] == 1
3109 assert first_entry["data"]["action"] == "relay_unfollow"
3111 assert first_entry["message"] ==
3112 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3115 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3116 Repo.insert(%ModerationLog{
3120 "nickname" => admin.nickname,
3123 action: "relay_follow",
3124 target: "https://example.org/relay"
3128 Repo.insert(%ModerationLog{
3131 "id" => moderator.id,
3132 "nickname" => moderator.nickname,
3135 action: "relay_unfollow",
3136 target: "https://example.org/relay"
3140 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3142 response1 = json_response(conn1, 200)
3143 [first_entry] = response1["items"]
3145 assert response1["total"] == 1
3146 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3149 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3150 ModerationLog.insert_log(%{
3152 action: "relay_follow",
3153 target: "https://example.org/relay"
3156 ModerationLog.insert_log(%{
3158 action: "relay_unfollow",
3159 target: "https://example.org/relay"
3162 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3164 response1 = json_response(conn1, 200)
3165 [first_entry] = response1["items"]
3167 assert response1["total"] == 1
3169 assert get_in(first_entry, ["data", "message"]) ==
3170 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3174 describe "GET /users/:nickname/credentials" do
3175 test "gets the user credentials", %{conn: conn} do
3176 user = insert(:user)
3177 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3179 response = assert json_response(conn, 200)
3180 assert response["email"] == user.email
3183 test "returns 403 if requested by a non-admin" do
3184 user = insert(:user)
3188 |> assign(:user, user)
3189 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3191 assert json_response(conn, :forbidden)
3195 describe "PATCH /users/:nickname/credentials" do
3196 test "changes password and email", %{conn: conn, admin: admin} do
3197 user = insert(:user)
3198 assert user.password_reset_pending == false
3201 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3202 "password" => "new_password",
3203 "email" => "new_email@example.com",
3204 "name" => "new_name"
3207 assert json_response(conn, 200) == %{"status" => "success"}
3209 ObanHelpers.perform_all()
3211 updated_user = User.get_by_id(user.id)
3213 assert updated_user.email == "new_email@example.com"
3214 assert updated_user.name == "new_name"
3215 assert updated_user.password_hash != user.password_hash
3216 assert updated_user.password_reset_pending == true
3218 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3220 assert ModerationLog.get_log_entry_message(log_entry1) ==
3221 "@#{admin.nickname} updated users: @#{user.nickname}"
3223 assert ModerationLog.get_log_entry_message(log_entry2) ==
3224 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3227 test "returns 403 if requested by a non-admin" do
3228 user = insert(:user)
3232 |> assign(:user, user)
3233 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3234 "password" => "new_password",
3235 "email" => "new_email@example.com",
3236 "name" => "new_name"
3239 assert json_response(conn, :forbidden)
3243 describe "PATCH /users/:nickname/force_password_reset" do
3244 test "sets password_reset_pending to true", %{conn: conn} do
3245 user = insert(:user)
3246 assert user.password_reset_pending == false
3249 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3251 assert json_response(conn, 204) == ""
3253 ObanHelpers.perform_all()
3255 assert User.get_by_id(user.id).password_reset_pending == true
3259 describe "relays" do
3260 test "POST /relay", %{conn: conn, admin: admin} do
3262 post(conn, "/api/pleroma/admin/relay", %{
3263 relay_url: "http://mastodon.example.org/users/admin"
3266 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3268 log_entry = Repo.one(ModerationLog)
3270 assert ModerationLog.get_log_entry_message(log_entry) ==
3271 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3274 test "GET /relay", %{conn: conn} do
3275 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3277 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3278 |> Enum.each(fn ap_id ->
3279 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3280 User.follow(relay_user, user)
3283 conn = get(conn, "/api/pleroma/admin/relay")
3285 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3288 test "DELETE /relay", %{conn: conn, admin: admin} do
3289 post(conn, "/api/pleroma/admin/relay", %{
3290 relay_url: "http://mastodon.example.org/users/admin"
3294 delete(conn, "/api/pleroma/admin/relay", %{
3295 relay_url: "http://mastodon.example.org/users/admin"
3298 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3300 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3302 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3303 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3305 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3306 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3310 describe "instances" do
3311 test "GET /instances/:instance/statuses", %{conn: conn} do
3312 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3313 user2 = insert(:user, local: false, nickname: "test@test.com")
3314 insert_pair(:note_activity, user: user)
3315 activity = insert(:note_activity, user: user2)
3317 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3319 response = json_response(ret_conn, 200)
3321 assert length(response) == 2
3323 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3325 response = json_response(ret_conn, 200)
3327 assert length(response) == 1
3329 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3331 response = json_response(ret_conn, 200)
3333 assert Enum.empty?(response)
3335 CommonAPI.repeat(activity.id, user)
3337 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3338 response = json_response(ret_conn, 200)
3339 assert length(response) == 2
3341 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3342 response = json_response(ret_conn, 200)
3343 assert length(response) == 3
3347 describe "PATCH /confirm_email" do
3348 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3349 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3351 assert first_user.confirmation_pending == true
3352 assert second_user.confirmation_pending == true
3355 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3357 first_user.nickname,
3358 second_user.nickname
3362 assert ret_conn.status == 200
3364 assert first_user.confirmation_pending == true
3365 assert second_user.confirmation_pending == true
3367 log_entry = Repo.one(ModerationLog)
3369 assert ModerationLog.get_log_entry_message(log_entry) ==
3370 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3371 second_user.nickname
3376 describe "PATCH /resend_confirmation_email" do
3377 test "it resend emails for two users", %{conn: conn, admin: admin} do
3378 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3381 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3383 first_user.nickname,
3384 second_user.nickname
3388 assert ret_conn.status == 200
3390 log_entry = Repo.one(ModerationLog)
3392 assert ModerationLog.get_log_entry_message(log_entry) ==
3393 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3394 second_user.nickname
3399 describe "POST /reports/:id/notes" do
3400 setup %{conn: conn, admin: admin} do
3401 [reporter, target_user] = insert_pair(:user)
3402 activity = insert(:note_activity, user: target_user)
3404 {:ok, %{id: report_id}} =
3405 CommonAPI.report(reporter, %{
3406 "account_id" => target_user.id,
3407 "comment" => "I feel offended",
3408 "status_ids" => [activity.id]
3411 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3412 content: "this is disgusting!"
3415 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3416 content: "this is disgusting2!"
3421 report_id: report_id
3425 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3426 [note, _] = Repo.all(ReportNote)
3429 activity_id: ^report_id,
3430 content: "this is disgusting!",
3435 test "it returns reports with notes", %{conn: conn, admin: admin} do
3436 conn = get(conn, "/api/pleroma/admin/reports")
3438 response = json_response(conn, 200)
3439 notes = hd(response["reports"])["notes"]
3442 assert note["user"]["nickname"] == admin.nickname
3443 assert note["content"] == "this is disgusting!"
3444 assert note["created_at"]
3445 assert response["total"] == 1
3448 test "it deletes the note", %{conn: conn, report_id: report_id} do
3449 assert ReportNote |> Repo.all() |> length() == 2
3451 [note, _] = Repo.all(ReportNote)
3453 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3455 assert ReportNote |> Repo.all() |> length() == 1
3459 test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do
3460 admin = insert(:user, is_admin: true)
3463 assign(conn, :user, admin)
3464 |> get("/api/pleroma/admin/config/descriptions")
3466 assert [child | _others] = json_response(conn, 200)
3468 assert child["children"]
3470 assert String.starts_with?(child["group"], ":")
3471 assert child["description"]
3474 describe "/api/pleroma/admin/stats" do
3475 test "status visibility count", %{conn: conn} do
3476 admin = insert(:user, is_admin: true)
3477 user = insert(:user)
3478 CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
3479 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3480 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3484 |> assign(:user, admin)
3485 |> get("/api/pleroma/admin/stats")
3486 |> json_response(200)
3488 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3489 response["status_visibility"]
3494 # Needed for testing
3495 defmodule Pleroma.Web.Endpoint.NotReal do
3498 defmodule Pleroma.Captcha.NotReal do