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.MastodonAPI.StatusView
25 alias Pleroma.Web.MediaProxy
28 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
34 admin = insert(:user, is_admin: true)
35 token = insert(:oauth_admin_token, user: admin)
39 |> assign(:user, admin)
40 |> assign(:token, token)
42 {:ok, %{admin: admin, token: token, conn: conn}}
45 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
46 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
48 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
51 url = "/api/pleroma/admin/users/#{user.nickname}"
53 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
54 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
55 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
57 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
58 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
61 for good_token <- [good_token1, good_token2, good_token3] do
64 |> assign(:user, admin)
65 |> assign(:token, good_token)
68 assert json_response(conn, 200)
71 for good_token <- [good_token1, good_token2, good_token3] do
75 |> assign(:token, good_token)
78 assert json_response(conn, :forbidden)
81 for bad_token <- [bad_token1, bad_token2, bad_token3] do
84 |> assign(:user, admin)
85 |> assign(:token, bad_token)
88 assert json_response(conn, :forbidden)
93 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
94 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
96 test "GET /api/pleroma/admin/users/:nickname requires " <>
97 "read:accounts or admin:read:accounts or broader scope",
100 url = "/api/pleroma/admin/users/#{user.nickname}"
102 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
103 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
104 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
105 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
106 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
108 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
110 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
111 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
114 for good_token <- good_tokens do
117 |> assign(:user, admin)
118 |> assign(:token, good_token)
121 assert json_response(conn, 200)
124 for good_token <- good_tokens do
127 |> assign(:user, nil)
128 |> assign(:token, good_token)
131 assert json_response(conn, :forbidden)
134 for bad_token <- [bad_token1, bad_token2, bad_token3] do
137 |> assign(:user, admin)
138 |> assign(:token, bad_token)
141 assert json_response(conn, :forbidden)
146 describe "DELETE /api/pleroma/admin/users" do
147 test "single user", %{admin: admin, conn: conn} do
152 |> put_req_header("accept", "application/json")
153 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
155 log_entry = Repo.one(ModerationLog)
157 assert ModerationLog.get_log_entry_message(log_entry) ==
158 "@#{admin.nickname} deleted users: @#{user.nickname}"
160 assert json_response(conn, 200) == user.nickname
163 test "multiple users", %{admin: admin, conn: conn} do
164 user_one = insert(:user)
165 user_two = insert(:user)
169 |> put_req_header("accept", "application/json")
170 |> delete("/api/pleroma/admin/users", %{
171 nicknames: [user_one.nickname, user_two.nickname]
174 log_entry = Repo.one(ModerationLog)
176 assert ModerationLog.get_log_entry_message(log_entry) ==
177 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
179 response = json_response(conn, 200)
180 assert response -- [user_one.nickname, user_two.nickname] == []
184 describe "/api/pleroma/admin/users" do
185 test "Create", %{conn: conn} do
188 |> put_req_header("accept", "application/json")
189 |> post("/api/pleroma/admin/users", %{
192 "nickname" => "lain",
193 "email" => "lain@example.org",
197 "nickname" => "lain2",
198 "email" => "lain2@example.org",
204 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
205 assert response == ["success", "success"]
207 log_entry = Repo.one(ModerationLog)
209 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
212 test "Cannot create user with existing email", %{conn: conn} do
217 |> put_req_header("accept", "application/json")
218 |> post("/api/pleroma/admin/users", %{
221 "nickname" => "lain",
222 "email" => user.email,
228 assert json_response(conn, 409) == [
232 "email" => user.email,
235 "error" => "email has already been taken",
241 test "Cannot create user with existing nickname", %{conn: conn} do
246 |> put_req_header("accept", "application/json")
247 |> post("/api/pleroma/admin/users", %{
250 "nickname" => user.nickname,
251 "email" => "someuser@plerama.social",
257 assert json_response(conn, 409) == [
261 "email" => "someuser@plerama.social",
262 "nickname" => user.nickname
264 "error" => "nickname has already been taken",
270 test "Multiple user creation works in transaction", %{conn: conn} do
275 |> put_req_header("accept", "application/json")
276 |> post("/api/pleroma/admin/users", %{
279 "nickname" => "newuser",
280 "email" => "newuser@pleroma.social",
284 "nickname" => "lain",
285 "email" => user.email,
291 assert json_response(conn, 409) == [
295 "email" => user.email,
298 "error" => "email has already been taken",
304 "email" => "newuser@pleroma.social",
305 "nickname" => "newuser"
312 assert User.get_by_nickname("newuser") === nil
316 describe "/api/pleroma/admin/users/:nickname" do
317 test "Show", %{conn: conn} do
320 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
323 "deactivated" => false,
324 "id" => to_string(user.id),
326 "nickname" => user.nickname,
327 "roles" => %{"admin" => false, "moderator" => false},
329 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
330 "display_name" => HTML.strip_tags(user.name || user.nickname),
331 "confirmation_pending" => false
334 assert expected == json_response(conn, 200)
337 test "when the user doesn't exist", %{conn: conn} do
340 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
342 assert "Not found" == json_response(conn, 404)
346 describe "/api/pleroma/admin/users/follow" do
347 test "allows to force-follow another user", %{admin: admin, conn: conn} do
349 follower = insert(:user)
352 |> put_req_header("accept", "application/json")
353 |> post("/api/pleroma/admin/users/follow", %{
354 "follower" => follower.nickname,
355 "followed" => user.nickname
358 user = User.get_cached_by_id(user.id)
359 follower = User.get_cached_by_id(follower.id)
361 assert User.following?(follower, user)
363 log_entry = Repo.one(ModerationLog)
365 assert ModerationLog.get_log_entry_message(log_entry) ==
366 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
370 describe "/api/pleroma/admin/users/unfollow" do
371 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
373 follower = insert(:user)
375 User.follow(follower, user)
378 |> put_req_header("accept", "application/json")
379 |> post("/api/pleroma/admin/users/unfollow", %{
380 "follower" => follower.nickname,
381 "followed" => user.nickname
384 user = User.get_cached_by_id(user.id)
385 follower = User.get_cached_by_id(follower.id)
387 refute User.following?(follower, user)
389 log_entry = Repo.one(ModerationLog)
391 assert ModerationLog.get_log_entry_message(log_entry) ==
392 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
396 describe "PUT /api/pleroma/admin/users/tag" do
397 setup %{conn: conn} do
398 user1 = insert(:user, %{tags: ["x"]})
399 user2 = insert(:user, %{tags: ["y"]})
400 user3 = insert(:user, %{tags: ["unchanged"]})
404 |> put_req_header("accept", "application/json")
406 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
407 "#{user2.nickname}&tags[]=foo&tags[]=bar"
410 %{conn: conn, user1: user1, user2: user2, user3: user3}
413 test "it appends specified tags to users with specified nicknames", %{
419 assert json_response(conn, :no_content)
420 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
421 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
423 log_entry = Repo.one(ModerationLog)
426 [user1.nickname, user2.nickname]
427 |> Enum.map(&"@#{&1}")
430 tags = ["foo", "bar"] |> Enum.join(", ")
432 assert ModerationLog.get_log_entry_message(log_entry) ==
433 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
436 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
437 assert json_response(conn, :no_content)
438 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
442 describe "DELETE /api/pleroma/admin/users/tag" do
443 setup %{conn: conn} do
444 user1 = insert(:user, %{tags: ["x"]})
445 user2 = insert(:user, %{tags: ["y", "z"]})
446 user3 = insert(:user, %{tags: ["unchanged"]})
450 |> put_req_header("accept", "application/json")
452 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
453 "#{user2.nickname}&tags[]=x&tags[]=z"
456 %{conn: conn, user1: user1, user2: user2, user3: user3}
459 test "it removes specified tags from users with specified nicknames", %{
465 assert json_response(conn, :no_content)
466 assert User.get_cached_by_id(user1.id).tags == []
467 assert User.get_cached_by_id(user2.id).tags == ["y"]
469 log_entry = Repo.one(ModerationLog)
472 [user1.nickname, user2.nickname]
473 |> Enum.map(&"@#{&1}")
476 tags = ["x", "z"] |> Enum.join(", ")
478 assert ModerationLog.get_log_entry_message(log_entry) ==
479 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
482 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
483 assert json_response(conn, :no_content)
484 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
488 describe "/api/pleroma/admin/users/:nickname/permission_group" do
489 test "GET is giving user_info", %{admin: admin, conn: conn} do
492 |> put_req_header("accept", "application/json")
493 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
495 assert json_response(conn, 200) == %{
497 "is_moderator" => false
501 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
506 |> put_req_header("accept", "application/json")
507 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
509 assert json_response(conn, 200) == %{
513 log_entry = Repo.one(ModerationLog)
515 assert ModerationLog.get_log_entry_message(log_entry) ==
516 "@#{admin.nickname} made @#{user.nickname} admin"
519 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
520 user_one = insert(:user)
521 user_two = insert(:user)
525 |> put_req_header("accept", "application/json")
526 |> post("/api/pleroma/admin/users/permission_group/admin", %{
527 nicknames: [user_one.nickname, user_two.nickname]
530 assert json_response(conn, 200) == %{"is_admin" => true}
532 log_entry = Repo.one(ModerationLog)
534 assert ModerationLog.get_log_entry_message(log_entry) ==
535 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
538 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
539 user = insert(:user, is_admin: true)
543 |> put_req_header("accept", "application/json")
544 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
546 assert json_response(conn, 200) == %{"is_admin" => false}
548 log_entry = Repo.one(ModerationLog)
550 assert ModerationLog.get_log_entry_message(log_entry) ==
551 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
554 test "/:right DELETE, can remove from a permission group (multiple)", %{
558 user_one = insert(:user, is_admin: true)
559 user_two = insert(:user, is_admin: true)
563 |> put_req_header("accept", "application/json")
564 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
565 nicknames: [user_one.nickname, user_two.nickname]
568 assert json_response(conn, 200) == %{"is_admin" => false}
570 log_entry = Repo.one(ModerationLog)
572 assert ModerationLog.get_log_entry_message(log_entry) ==
573 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
579 describe "POST /api/pleroma/admin/email_invite, with valid config" do
580 setup do: clear_config([:instance, :registrations_open], false)
581 setup do: clear_config([:instance, :invites_enabled], true)
583 test "sends invitation and returns 204", %{admin: admin, conn: conn} do
584 recipient_email = "foo@bar.com"
585 recipient_name = "J. D."
590 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
593 assert json_response(conn, :no_content)
595 token_record = List.last(Repo.all(Pleroma.UserInviteToken))
597 refute token_record.used
599 notify_email = Config.get([:instance, :notify_email])
600 instance_name = Config.get([:instance, :name])
603 Pleroma.Emails.UserEmail.user_invitation_email(
610 Swoosh.TestAssertions.assert_email_sent(
611 from: {instance_name, notify_email},
612 to: {recipient_name, recipient_email},
613 html_body: email.html_body
617 test "it returns 403 if requested by a non-admin" do
618 non_admin_user = insert(:user)
619 token = insert(:oauth_token, user: non_admin_user)
623 |> assign(:user, non_admin_user)
624 |> assign(:token, token)
625 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
627 assert json_response(conn, :forbidden)
631 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
632 setup do: clear_config([:instance, :registrations_open])
633 setup do: clear_config([:instance, :invites_enabled])
635 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
636 Config.put([:instance, :registrations_open], false)
637 Config.put([:instance, :invites_enabled], false)
639 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
641 assert json_response(conn, :internal_server_error)
644 test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
645 Config.put([:instance, :registrations_open], true)
646 Config.put([:instance, :invites_enabled], true)
648 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
650 assert json_response(conn, :internal_server_error)
654 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
659 |> put_req_header("accept", "application/json")
660 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
662 resp = json_response(conn, 200)
664 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
667 describe "GET /api/pleroma/admin/users" do
668 test "renders users array for the first page", %{conn: conn, admin: admin} do
669 user = insert(:user, local: false, tags: ["foo", "bar"])
670 conn = get(conn, "/api/pleroma/admin/users?page=1")
675 "deactivated" => admin.deactivated,
677 "nickname" => admin.nickname,
678 "roles" => %{"admin" => true, "moderator" => false},
681 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
682 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
683 "confirmation_pending" => false
686 "deactivated" => user.deactivated,
688 "nickname" => user.nickname,
689 "roles" => %{"admin" => false, "moderator" => false},
691 "tags" => ["foo", "bar"],
692 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
693 "display_name" => HTML.strip_tags(user.name || user.nickname),
694 "confirmation_pending" => false
697 |> Enum.sort_by(& &1["nickname"])
699 assert json_response(conn, 200) == %{
706 test "renders empty array for the second page", %{conn: conn} do
709 conn = get(conn, "/api/pleroma/admin/users?page=2")
711 assert json_response(conn, 200) == %{
718 test "regular search", %{conn: conn} do
719 user = insert(:user, nickname: "bob")
721 conn = get(conn, "/api/pleroma/admin/users?query=bo")
723 assert json_response(conn, 200) == %{
728 "deactivated" => user.deactivated,
730 "nickname" => user.nickname,
731 "roles" => %{"admin" => false, "moderator" => false},
734 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
735 "display_name" => HTML.strip_tags(user.name || user.nickname),
736 "confirmation_pending" => false
742 test "search by domain", %{conn: conn} do
743 user = insert(:user, nickname: "nickname@domain.com")
746 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
748 assert json_response(conn, 200) == %{
753 "deactivated" => user.deactivated,
755 "nickname" => user.nickname,
756 "roles" => %{"admin" => false, "moderator" => false},
759 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
760 "display_name" => HTML.strip_tags(user.name || user.nickname),
761 "confirmation_pending" => false
767 test "search by full nickname", %{conn: conn} do
768 user = insert(:user, nickname: "nickname@domain.com")
771 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
773 assert json_response(conn, 200) == %{
778 "deactivated" => user.deactivated,
780 "nickname" => user.nickname,
781 "roles" => %{"admin" => false, "moderator" => false},
784 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
785 "display_name" => HTML.strip_tags(user.name || user.nickname),
786 "confirmation_pending" => false
792 test "search by display name", %{conn: conn} do
793 user = insert(:user, name: "Display name")
796 conn = get(conn, "/api/pleroma/admin/users?name=display")
798 assert json_response(conn, 200) == %{
803 "deactivated" => user.deactivated,
805 "nickname" => user.nickname,
806 "roles" => %{"admin" => false, "moderator" => false},
809 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
810 "display_name" => HTML.strip_tags(user.name || user.nickname),
811 "confirmation_pending" => false
817 test "search by email", %{conn: conn} do
818 user = insert(:user, email: "email@example.com")
821 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
823 assert json_response(conn, 200) == %{
828 "deactivated" => user.deactivated,
830 "nickname" => user.nickname,
831 "roles" => %{"admin" => false, "moderator" => false},
834 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
835 "display_name" => HTML.strip_tags(user.name || user.nickname),
836 "confirmation_pending" => false
842 test "regular search with page size", %{conn: conn} do
843 user = insert(:user, nickname: "aalice")
844 user2 = insert(:user, nickname: "alice")
846 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
848 assert json_response(conn1, 200) == %{
853 "deactivated" => user.deactivated,
855 "nickname" => user.nickname,
856 "roles" => %{"admin" => false, "moderator" => false},
859 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
860 "display_name" => HTML.strip_tags(user.name || user.nickname),
861 "confirmation_pending" => false
866 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
868 assert json_response(conn2, 200) == %{
873 "deactivated" => user2.deactivated,
875 "nickname" => user2.nickname,
876 "roles" => %{"admin" => false, "moderator" => false},
879 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
880 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
881 "confirmation_pending" => false
887 test "only local users" do
888 admin = insert(:user, is_admin: true, nickname: "john")
889 token = insert(:oauth_admin_token, user: admin)
890 user = insert(:user, nickname: "bob")
892 insert(:user, nickname: "bobb", local: false)
896 |> assign(:user, admin)
897 |> assign(:token, token)
898 |> get("/api/pleroma/admin/users?query=bo&filters=local")
900 assert json_response(conn, 200) == %{
905 "deactivated" => user.deactivated,
907 "nickname" => user.nickname,
908 "roles" => %{"admin" => false, "moderator" => false},
911 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
912 "display_name" => HTML.strip_tags(user.name || user.nickname),
913 "confirmation_pending" => false
919 test "only local users with no query", %{conn: conn, admin: old_admin} do
920 admin = insert(:user, is_admin: true, nickname: "john")
921 user = insert(:user, nickname: "bob")
923 insert(:user, nickname: "bobb", local: false)
925 conn = get(conn, "/api/pleroma/admin/users?filters=local")
930 "deactivated" => user.deactivated,
932 "nickname" => user.nickname,
933 "roles" => %{"admin" => false, "moderator" => false},
936 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
937 "display_name" => HTML.strip_tags(user.name || user.nickname),
938 "confirmation_pending" => false
941 "deactivated" => admin.deactivated,
943 "nickname" => admin.nickname,
944 "roles" => %{"admin" => true, "moderator" => false},
947 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
948 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
949 "confirmation_pending" => false
952 "deactivated" => false,
953 "id" => old_admin.id,
955 "nickname" => old_admin.nickname,
956 "roles" => %{"admin" => true, "moderator" => false},
958 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
959 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
960 "confirmation_pending" => false
963 |> Enum.sort_by(& &1["nickname"])
965 assert json_response(conn, 200) == %{
972 test "load only admins", %{conn: conn, admin: admin} do
973 second_admin = insert(:user, is_admin: true)
977 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
982 "deactivated" => false,
984 "nickname" => admin.nickname,
985 "roles" => %{"admin" => true, "moderator" => false},
986 "local" => admin.local,
988 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
989 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
990 "confirmation_pending" => false
993 "deactivated" => false,
994 "id" => second_admin.id,
995 "nickname" => second_admin.nickname,
996 "roles" => %{"admin" => true, "moderator" => false},
997 "local" => second_admin.local,
999 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1000 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1001 "confirmation_pending" => false
1004 |> Enum.sort_by(& &1["nickname"])
1006 assert json_response(conn, 200) == %{
1013 test "load only moderators", %{conn: conn} do
1014 moderator = insert(:user, is_moderator: true)
1018 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1020 assert json_response(conn, 200) == %{
1025 "deactivated" => false,
1026 "id" => moderator.id,
1027 "nickname" => moderator.nickname,
1028 "roles" => %{"admin" => false, "moderator" => true},
1029 "local" => moderator.local,
1031 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1032 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1033 "confirmation_pending" => false
1039 test "load users with tags list", %{conn: conn} do
1040 user1 = insert(:user, tags: ["first"])
1041 user2 = insert(:user, tags: ["second"])
1045 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1050 "deactivated" => false,
1052 "nickname" => user1.nickname,
1053 "roles" => %{"admin" => false, "moderator" => false},
1054 "local" => user1.local,
1055 "tags" => ["first"],
1056 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1057 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1058 "confirmation_pending" => false
1061 "deactivated" => false,
1063 "nickname" => user2.nickname,
1064 "roles" => %{"admin" => false, "moderator" => false},
1065 "local" => user2.local,
1066 "tags" => ["second"],
1067 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1068 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1069 "confirmation_pending" => false
1072 |> Enum.sort_by(& &1["nickname"])
1074 assert json_response(conn, 200) == %{
1081 test "it works with multiple filters" do
1082 admin = insert(:user, nickname: "john", is_admin: true)
1083 token = insert(:oauth_admin_token, user: admin)
1084 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1086 insert(:user, nickname: "ken", local: true, deactivated: true)
1087 insert(:user, nickname: "bobb", local: false, deactivated: false)
1091 |> assign(:user, admin)
1092 |> assign(:token, token)
1093 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1095 assert json_response(conn, 200) == %{
1100 "deactivated" => user.deactivated,
1102 "nickname" => user.nickname,
1103 "roles" => %{"admin" => false, "moderator" => false},
1104 "local" => user.local,
1106 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1107 "display_name" => HTML.strip_tags(user.name || user.nickname),
1108 "confirmation_pending" => false
1114 test "it omits relay user", %{admin: admin, conn: conn} do
1115 assert %User{} = Relay.get_actor()
1117 conn = get(conn, "/api/pleroma/admin/users")
1119 assert json_response(conn, 200) == %{
1124 "deactivated" => admin.deactivated,
1126 "nickname" => admin.nickname,
1127 "roles" => %{"admin" => true, "moderator" => false},
1130 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1131 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1132 "confirmation_pending" => false
1139 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1140 user_one = insert(:user, deactivated: true)
1141 user_two = insert(:user, deactivated: true)
1146 "/api/pleroma/admin/users/activate",
1147 %{nicknames: [user_one.nickname, user_two.nickname]}
1150 response = json_response(conn, 200)
1151 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1153 log_entry = Repo.one(ModerationLog)
1155 assert ModerationLog.get_log_entry_message(log_entry) ==
1156 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1159 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1160 user_one = insert(:user, deactivated: false)
1161 user_two = insert(:user, deactivated: false)
1166 "/api/pleroma/admin/users/deactivate",
1167 %{nicknames: [user_one.nickname, user_two.nickname]}
1170 response = json_response(conn, 200)
1171 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1173 log_entry = Repo.one(ModerationLog)
1175 assert ModerationLog.get_log_entry_message(log_entry) ==
1176 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1179 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1180 user = insert(:user)
1182 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1184 assert json_response(conn, 200) ==
1186 "deactivated" => !user.deactivated,
1188 "nickname" => user.nickname,
1189 "roles" => %{"admin" => false, "moderator" => false},
1192 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1193 "display_name" => HTML.strip_tags(user.name || user.nickname),
1194 "confirmation_pending" => false
1197 log_entry = Repo.one(ModerationLog)
1199 assert ModerationLog.get_log_entry_message(log_entry) ==
1200 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1203 describe "POST /api/pleroma/admin/users/invite_token" do
1204 test "without options", %{conn: conn} do
1205 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1207 invite_json = json_response(conn, 200)
1208 invite = UserInviteToken.find_by_token!(invite_json["token"])
1210 refute invite.expires_at
1211 refute invite.max_use
1212 assert invite.invite_type == "one_time"
1215 test "with expires_at", %{conn: conn} do
1217 post(conn, "/api/pleroma/admin/users/invite_token", %{
1218 "expires_at" => Date.to_string(Date.utc_today())
1221 invite_json = json_response(conn, 200)
1222 invite = UserInviteToken.find_by_token!(invite_json["token"])
1225 assert invite.expires_at == Date.utc_today()
1226 refute invite.max_use
1227 assert invite.invite_type == "date_limited"
1230 test "with max_use", %{conn: conn} do
1231 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1233 invite_json = json_response(conn, 200)
1234 invite = UserInviteToken.find_by_token!(invite_json["token"])
1236 refute invite.expires_at
1237 assert invite.max_use == 150
1238 assert invite.invite_type == "reusable"
1241 test "with max use and expires_at", %{conn: conn} do
1243 post(conn, "/api/pleroma/admin/users/invite_token", %{
1245 "expires_at" => Date.to_string(Date.utc_today())
1248 invite_json = json_response(conn, 200)
1249 invite = UserInviteToken.find_by_token!(invite_json["token"])
1251 assert invite.expires_at == Date.utc_today()
1252 assert invite.max_use == 150
1253 assert invite.invite_type == "reusable_date_limited"
1257 describe "GET /api/pleroma/admin/users/invites" do
1258 test "no invites", %{conn: conn} do
1259 conn = get(conn, "/api/pleroma/admin/users/invites")
1261 assert json_response(conn, 200) == %{"invites" => []}
1264 test "with invite", %{conn: conn} do
1265 {:ok, invite} = UserInviteToken.create_invite()
1267 conn = get(conn, "/api/pleroma/admin/users/invites")
1269 assert json_response(conn, 200) == %{
1272 "expires_at" => nil,
1274 "invite_type" => "one_time",
1276 "token" => invite.token,
1285 describe "POST /api/pleroma/admin/users/revoke_invite" do
1286 test "with token", %{conn: conn} do
1287 {:ok, invite} = UserInviteToken.create_invite()
1289 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1291 assert json_response(conn, 200) == %{
1292 "expires_at" => nil,
1294 "invite_type" => "one_time",
1296 "token" => invite.token,
1302 test "with invalid token", %{conn: conn} do
1303 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1305 assert json_response(conn, :not_found) == "Not found"
1309 describe "GET /api/pleroma/admin/reports/:id" do
1310 test "returns report by its id", %{conn: conn} do
1311 [reporter, target_user] = insert_pair(:user)
1312 activity = insert(:note_activity, user: target_user)
1314 {:ok, %{id: report_id}} =
1315 CommonAPI.report(reporter, %{
1316 "account_id" => target_user.id,
1317 "comment" => "I feel offended",
1318 "status_ids" => [activity.id]
1323 |> get("/api/pleroma/admin/reports/#{report_id}")
1324 |> json_response(:ok)
1326 assert response["id"] == report_id
1329 test "returns 404 when report id is invalid", %{conn: conn} do
1330 conn = get(conn, "/api/pleroma/admin/reports/test")
1332 assert json_response(conn, :not_found) == "Not found"
1336 describe "PATCH /api/pleroma/admin/reports" do
1338 [reporter, target_user] = insert_pair(:user)
1339 activity = insert(:note_activity, user: target_user)
1341 {:ok, %{id: report_id}} =
1342 CommonAPI.report(reporter, %{
1343 "account_id" => target_user.id,
1344 "comment" => "I feel offended",
1345 "status_ids" => [activity.id]
1348 {:ok, %{id: second_report_id}} =
1349 CommonAPI.report(reporter, %{
1350 "account_id" => target_user.id,
1351 "comment" => "I feel very offended",
1352 "status_ids" => [activity.id]
1357 second_report_id: second_report_id
1361 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1362 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1363 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1367 |> assign(:token, read_token)
1368 |> patch("/api/pleroma/admin/reports", %{
1369 "reports" => [%{"state" => "resolved", "id" => id}]
1371 |> json_response(403)
1373 assert response == %{
1374 "error" => "Insufficient permissions: admin:write:reports."
1378 |> assign(:token, write_token)
1379 |> patch("/api/pleroma/admin/reports", %{
1380 "reports" => [%{"state" => "resolved", "id" => id}]
1382 |> json_response(:no_content)
1385 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1387 |> patch("/api/pleroma/admin/reports", %{
1389 %{"state" => "resolved", "id" => id}
1392 |> json_response(:no_content)
1394 activity = Activity.get_by_id(id)
1395 assert activity.data["state"] == "resolved"
1397 log_entry = Repo.one(ModerationLog)
1399 assert ModerationLog.get_log_entry_message(log_entry) ==
1400 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1403 test "closes report", %{conn: conn, id: id, admin: admin} do
1405 |> patch("/api/pleroma/admin/reports", %{
1407 %{"state" => "closed", "id" => id}
1410 |> json_response(:no_content)
1412 activity = Activity.get_by_id(id)
1413 assert activity.data["state"] == "closed"
1415 log_entry = Repo.one(ModerationLog)
1417 assert ModerationLog.get_log_entry_message(log_entry) ==
1418 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1421 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1424 |> patch("/api/pleroma/admin/reports", %{
1426 %{"state" => "test", "id" => id}
1430 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1433 test "returns 404 when report is not exist", %{conn: conn} do
1436 |> patch("/api/pleroma/admin/reports", %{
1438 %{"state" => "closed", "id" => "test"}
1442 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1445 test "updates state of multiple reports", %{
1449 second_report_id: second_report_id
1452 |> patch("/api/pleroma/admin/reports", %{
1454 %{"state" => "resolved", "id" => id},
1455 %{"state" => "closed", "id" => second_report_id}
1458 |> json_response(:no_content)
1460 activity = Activity.get_by_id(id)
1461 second_activity = Activity.get_by_id(second_report_id)
1462 assert activity.data["state"] == "resolved"
1463 assert second_activity.data["state"] == "closed"
1465 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1467 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1468 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1470 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1471 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1475 describe "GET /api/pleroma/admin/reports" do
1476 test "returns empty response when no reports created", %{conn: conn} do
1479 |> get("/api/pleroma/admin/reports")
1480 |> json_response(:ok)
1482 assert Enum.empty?(response["reports"])
1483 assert response["total"] == 0
1486 test "returns reports", %{conn: conn} do
1487 [reporter, target_user] = insert_pair(:user)
1488 activity = insert(:note_activity, user: target_user)
1490 {:ok, %{id: report_id}} =
1491 CommonAPI.report(reporter, %{
1492 "account_id" => target_user.id,
1493 "comment" => "I feel offended",
1494 "status_ids" => [activity.id]
1499 |> get("/api/pleroma/admin/reports")
1500 |> json_response(:ok)
1502 [report] = response["reports"]
1504 assert length(response["reports"]) == 1
1505 assert report["id"] == report_id
1507 assert response["total"] == 1
1510 test "returns reports with specified state", %{conn: conn} do
1511 [reporter, target_user] = insert_pair(:user)
1512 activity = insert(:note_activity, user: target_user)
1514 {:ok, %{id: first_report_id}} =
1515 CommonAPI.report(reporter, %{
1516 "account_id" => target_user.id,
1517 "comment" => "I feel offended",
1518 "status_ids" => [activity.id]
1521 {:ok, %{id: second_report_id}} =
1522 CommonAPI.report(reporter, %{
1523 "account_id" => target_user.id,
1524 "comment" => "I don't like this user"
1527 CommonAPI.update_report_state(second_report_id, "closed")
1531 |> get("/api/pleroma/admin/reports", %{
1534 |> json_response(:ok)
1536 [open_report] = response["reports"]
1538 assert length(response["reports"]) == 1
1539 assert open_report["id"] == first_report_id
1541 assert response["total"] == 1
1545 |> get("/api/pleroma/admin/reports", %{
1548 |> json_response(:ok)
1550 [closed_report] = response["reports"]
1552 assert length(response["reports"]) == 1
1553 assert closed_report["id"] == second_report_id
1555 assert response["total"] == 1
1559 |> get("/api/pleroma/admin/reports", %{
1560 "state" => "resolved"
1562 |> json_response(:ok)
1564 assert Enum.empty?(response["reports"])
1565 assert response["total"] == 0
1568 test "returns 403 when requested by a non-admin" do
1569 user = insert(:user)
1570 token = insert(:oauth_token, user: user)
1574 |> assign(:user, user)
1575 |> assign(:token, token)
1576 |> get("/api/pleroma/admin/reports")
1578 assert json_response(conn, :forbidden) ==
1579 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1582 test "returns 403 when requested by anonymous" do
1583 conn = get(build_conn(), "/api/pleroma/admin/reports")
1585 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1589 describe "GET /api/pleroma/admin/grouped_reports" do
1591 [reporter, target_user] = insert_pair(:user)
1593 date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1594 date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1595 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1598 insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
1601 insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
1604 insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
1606 {:ok, first_report} =
1607 CommonAPI.report(reporter, %{
1608 "account_id" => target_user.id,
1609 "status_ids" => [first_status.id, second_status.id, third_status.id]
1612 {:ok, second_report} =
1613 CommonAPI.report(reporter, %{
1614 "account_id" => target_user.id,
1615 "status_ids" => [first_status.id, second_status.id]
1618 {:ok, third_report} =
1619 CommonAPI.report(reporter, %{
1620 "account_id" => target_user.id,
1621 "status_ids" => [first_status.id]
1625 first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
1626 second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
1627 third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
1628 first_report: first_report,
1629 first_status_reports: [first_report, second_report, third_report],
1630 second_status_reports: [first_report, second_report],
1631 third_status_reports: [first_report],
1632 target_user: target_user,
1637 test "returns reports grouped by status", %{
1639 first_status: first_status,
1640 second_status: second_status,
1641 third_status: third_status,
1642 first_status_reports: first_status_reports,
1643 second_status_reports: second_status_reports,
1644 third_status_reports: third_status_reports,
1645 target_user: target_user,
1650 |> get("/api/pleroma/admin/grouped_reports")
1651 |> json_response(:ok)
1653 assert length(response["reports"]) == 3
1655 first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
1657 second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id))
1659 third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id))
1661 assert length(first_group["reports"]) == 3
1662 assert length(second_group["reports"]) == 2
1663 assert length(third_group["reports"]) == 1
1665 assert first_group["date"] ==
1666 Enum.max_by(first_status_reports, fn act ->
1667 NaiveDateTime.from_iso8601!(act.data["published"])
1668 end).data["published"]
1670 assert first_group["status"] ==
1672 stringify_keys(StatusView.render("show.json", %{activity: first_status})),
1677 assert(first_group["account"]["id"] == target_user.id)
1679 assert length(first_group["actors"]) == 1
1680 assert hd(first_group["actors"])["id"] == reporter.id
1682 assert Enum.map(first_group["reports"], & &1["id"]) --
1683 Enum.map(first_status_reports, & &1.id) == []
1685 assert second_group["date"] ==
1686 Enum.max_by(second_status_reports, fn act ->
1687 NaiveDateTime.from_iso8601!(act.data["published"])
1688 end).data["published"]
1690 assert second_group["status"] ==
1692 stringify_keys(StatusView.render("show.json", %{activity: second_status})),
1697 assert second_group["account"]["id"] == target_user.id
1699 assert length(second_group["actors"]) == 1
1700 assert hd(second_group["actors"])["id"] == reporter.id
1702 assert Enum.map(second_group["reports"], & &1["id"]) --
1703 Enum.map(second_status_reports, & &1.id) == []
1705 assert third_group["date"] ==
1706 Enum.max_by(third_status_reports, fn act ->
1707 NaiveDateTime.from_iso8601!(act.data["published"])
1708 end).data["published"]
1710 assert third_group["status"] ==
1712 stringify_keys(StatusView.render("show.json", %{activity: third_status})),
1717 assert third_group["account"]["id"] == target_user.id
1719 assert length(third_group["actors"]) == 1
1720 assert hd(third_group["actors"])["id"] == reporter.id
1722 assert Enum.map(third_group["reports"], & &1["id"]) --
1723 Enum.map(third_status_reports, & &1.id) == []
1726 test "reopened report renders status data", %{
1728 first_report: first_report,
1729 first_status: first_status
1731 {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
1735 |> get("/api/pleroma/admin/grouped_reports")
1736 |> json_response(:ok)
1738 first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
1740 assert first_group["status"] ==
1742 stringify_keys(StatusView.render("show.json", %{activity: first_status})),
1748 test "reopened report does not render status data if status has been deleted", %{
1750 first_report: first_report,
1751 first_status: first_status,
1752 target_user: target_user
1754 {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
1755 {:ok, _} = CommonAPI.delete(first_status.id, target_user)
1757 refute Activity.get_by_ap_id(first_status.id)
1761 |> get("/api/pleroma/admin/grouped_reports")
1762 |> json_response(:ok)
1764 assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["status"][
1768 assert length(Enum.filter(response["reports"], &(&1["status"]["deleted"] == false))) == 2
1771 test "account not empty if status was deleted", %{
1773 first_report: first_report,
1774 first_status: first_status,
1775 target_user: target_user
1777 {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
1778 {:ok, _} = CommonAPI.delete(first_status.id, target_user)
1780 refute Activity.get_by_ap_id(first_status.id)
1784 |> get("/api/pleroma/admin/grouped_reports")
1785 |> json_response(:ok)
1787 assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["account"]
1791 describe "PUT /api/pleroma/admin/statuses/:id" do
1793 activity = insert(:note_activity)
1798 test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
1801 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
1802 |> json_response(:ok)
1804 assert response["sensitive"]
1806 log_entry = Repo.one(ModerationLog)
1808 assert ModerationLog.get_log_entry_message(log_entry) ==
1809 "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
1813 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
1814 |> json_response(:ok)
1816 refute response["sensitive"]
1819 test "change visibility flag", %{conn: conn, id: id, admin: admin} do
1822 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
1823 |> json_response(:ok)
1825 assert response["visibility"] == "public"
1827 log_entry = Repo.one(ModerationLog)
1829 assert ModerationLog.get_log_entry_message(log_entry) ==
1830 "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
1834 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
1835 |> json_response(:ok)
1837 assert response["visibility"] == "private"
1841 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
1842 |> json_response(:ok)
1844 assert response["visibility"] == "unlisted"
1847 test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
1848 conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})
1850 assert json_response(conn, :bad_request) == "Unsupported visibility"
1854 describe "DELETE /api/pleroma/admin/statuses/:id" do
1856 activity = insert(:note_activity)
1861 test "deletes status", %{conn: conn, id: id, admin: admin} do
1863 |> delete("/api/pleroma/admin/statuses/#{id}")
1864 |> json_response(:ok)
1866 refute Activity.get_by_id(id)
1868 log_entry = Repo.one(ModerationLog)
1870 assert ModerationLog.get_log_entry_message(log_entry) ==
1871 "@#{admin.nickname} deleted status ##{id}"
1874 test "returns 404 when the status does not exist", %{conn: conn} do
1875 conn = delete(conn, "/api/pleroma/admin/statuses/test")
1877 assert json_response(conn, :not_found) == "Not found"
1881 describe "GET /api/pleroma/admin/config" do
1882 setup do: clear_config(:configurable_from_database, true)
1884 test "when configuration from database is off", %{conn: conn} do
1885 Config.put(:configurable_from_database, false)
1886 conn = get(conn, "/api/pleroma/admin/config")
1888 assert json_response(conn, 400) ==
1889 "To use this endpoint you need to enable configuration from database."
1892 test "with settings only in db", %{conn: conn} do
1893 config1 = insert(:config)
1894 config2 = insert(:config)
1896 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1901 "group" => ":pleroma",
1906 "group" => ":pleroma",
1911 } = json_response(conn, 200)
1913 assert key1 == config1.key
1914 assert key2 == config2.key
1917 test "db is added to settings that are in db", %{conn: conn} do
1918 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1920 %{"configs" => configs} =
1922 |> get("/api/pleroma/admin/config")
1923 |> json_response(200)
1926 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1927 group == ":pleroma" and key == ":instance"
1930 assert instance_config["db"] == [":name"]
1933 test "merged default setting with db settings", %{conn: conn} do
1934 config1 = insert(:config)
1935 config2 = insert(:config)
1939 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1942 %{"configs" => configs} =
1944 |> get("/api/pleroma/admin/config")
1945 |> json_response(200)
1947 assert length(configs) > 3
1950 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1951 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1954 assert length(received_configs) == 3
1958 |> ConfigDB.from_binary()
1960 |> ConfigDB.convert()
1962 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1963 assert db in [[config1.key], [config2.key], db_keys]
1966 ConfigDB.from_binary_with_convert(config1.value),
1967 ConfigDB.from_binary_with_convert(config2.value),
1968 ConfigDB.from_binary_with_convert(config3.value)
1973 test "subkeys with full update right merge", %{conn: conn} do
1977 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1983 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1986 %{"configs" => configs} =
1988 |> get("/api/pleroma/admin/config")
1989 |> json_response(200)
1992 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1993 group == ":pleroma" and key in [config1.key, config2.key]
1996 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
1997 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
1999 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
2000 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
2002 assert emoji_val[:groups] == [a: 1, b: 2]
2003 assert assets_val[:mascots] == [a: 1, b: 2]
2007 test "POST /api/pleroma/admin/config error", %{conn: conn} do
2008 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
2010 assert json_response(conn, 400) ==
2011 "To use this endpoint you need to enable configuration from database."
2014 describe "POST /api/pleroma/admin/config" do
2016 http = Application.get_env(:pleroma, :http)
2019 Application.delete_env(:pleroma, :key1)
2020 Application.delete_env(:pleroma, :key2)
2021 Application.delete_env(:pleroma, :key3)
2022 Application.delete_env(:pleroma, :key4)
2023 Application.delete_env(:pleroma, :keyaa1)
2024 Application.delete_env(:pleroma, :keyaa2)
2025 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
2026 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
2027 Application.put_env(:pleroma, :http, http)
2028 Application.put_env(:tesla, :adapter, Tesla.Mock)
2029 Restarter.Pleroma.refresh()
2033 setup do: clear_config(:configurable_from_database, true)
2035 @tag capture_log: true
2036 test "create new config setting in db", %{conn: conn} do
2037 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2038 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
2041 post(conn, "/api/pleroma/admin/config", %{
2043 %{group: ":pleroma", key: ":key1", value: "value1"},
2045 group: ":ueberauth",
2047 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
2053 ":nested_1" => "nested_value1",
2055 %{":nested_22" => "nested_value222"},
2056 %{":nested_33" => %{":nested_44" => "nested_444"}}
2064 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
2065 %{"nested_4" => true}
2071 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
2076 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
2081 assert json_response(conn, 200) == %{
2084 "group" => ":pleroma",
2086 "value" => "value1",
2090 "group" => ":ueberauth",
2091 "key" => "Ueberauth",
2092 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
2093 "db" => [":consumer_secret"]
2096 "group" => ":pleroma",
2099 ":nested_1" => "nested_value1",
2101 %{":nested_22" => "nested_value222"},
2102 %{":nested_33" => %{":nested_44" => "nested_444"}}
2108 "group" => ":pleroma",
2111 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
2112 %{"nested_4" => true}
2117 "group" => ":pleroma",
2119 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
2125 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
2131 assert Application.get_env(:pleroma, :key1) == "value1"
2133 assert Application.get_env(:pleroma, :key2) == %{
2134 nested_1: "nested_value1",
2136 %{nested_22: "nested_value222"},
2137 %{nested_33: %{nested_44: "nested_444"}}
2141 assert Application.get_env(:pleroma, :key3) == [
2142 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
2143 %{"nested_4" => true}
2146 assert Application.get_env(:pleroma, :key4) == %{
2147 "endpoint" => "https://example.com",
2151 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
2154 test "save configs setting without explicit key", %{conn: conn} do
2155 level = Application.get_env(:quack, :level)
2156 meta = Application.get_env(:quack, :meta)
2157 webhook_url = Application.get_env(:quack, :webhook_url)
2160 Application.put_env(:quack, :level, level)
2161 Application.put_env(:quack, :meta, meta)
2162 Application.put_env(:quack, :webhook_url, webhook_url)
2166 post(conn, "/api/pleroma/admin/config", %{
2180 key: ":webhook_url",
2181 value: "https://hooks.slack.com/services/KEY"
2186 assert json_response(conn, 200) == %{
2189 "group" => ":quack",
2195 "group" => ":quack",
2197 "value" => [":none"],
2201 "group" => ":quack",
2202 "key" => ":webhook_url",
2203 "value" => "https://hooks.slack.com/services/KEY",
2204 "db" => [":webhook_url"]
2209 assert Application.get_env(:quack, :level) == :info
2210 assert Application.get_env(:quack, :meta) == [:none]
2211 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
2214 test "saving config with partial update", %{conn: conn} do
2215 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
2218 post(conn, "/api/pleroma/admin/config", %{
2220 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
2224 assert json_response(conn, 200) == %{
2227 "group" => ":pleroma",
2230 %{"tuple" => [":key1", 1]},
2231 %{"tuple" => [":key2", 2]},
2232 %{"tuple" => [":key3", 3]}
2234 "db" => [":key1", ":key2", ":key3"]
2240 test "saving config which need pleroma reboot", %{conn: conn} do
2241 chat = Config.get(:chat)
2242 on_exit(fn -> Config.put(:chat, chat) end)
2246 "/api/pleroma/admin/config",
2249 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2253 |> json_response(200) == %{
2256 "db" => [":enabled"],
2257 "group" => ":pleroma",
2259 "value" => [%{"tuple" => [":enabled", true]}]
2262 "need_reboot" => true
2267 |> get("/api/pleroma/admin/config")
2268 |> json_response(200)
2270 assert configs["need_reboot"]
2273 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2274 end) =~ "pleroma restarted"
2278 |> get("/api/pleroma/admin/config")
2279 |> json_response(200)
2281 refute Map.has_key?(configs, "need_reboot")
2284 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
2285 chat = Config.get(:chat)
2286 on_exit(fn -> Config.put(:chat, chat) end)
2290 "/api/pleroma/admin/config",
2293 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2297 |> json_response(200) == %{
2300 "db" => [":enabled"],
2301 "group" => ":pleroma",
2303 "value" => [%{"tuple" => [":enabled", true]}]
2306 "need_reboot" => true
2309 assert post(conn, "/api/pleroma/admin/config", %{
2311 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
2314 |> json_response(200) == %{
2317 "group" => ":pleroma",
2320 %{"tuple" => [":key3", 3]}
2325 "need_reboot" => true
2329 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2330 end) =~ "pleroma restarted"
2334 |> get("/api/pleroma/admin/config")
2335 |> json_response(200)
2337 refute Map.has_key?(configs, "need_reboot")
2340 test "saving config with nested merge", %{conn: conn} do
2342 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
2345 post(conn, "/api/pleroma/admin/config", %{
2348 group: config.group,
2351 %{"tuple" => [":key3", 3]},
2356 %{"tuple" => [":k2", 1]},
2357 %{"tuple" => [":k3", 3]}
2366 assert json_response(conn, 200) == %{
2369 "group" => ":pleroma",
2372 %{"tuple" => [":key1", 1]},
2373 %{"tuple" => [":key3", 3]},
2378 %{"tuple" => [":k1", 1]},
2379 %{"tuple" => [":k2", 1]},
2380 %{"tuple" => [":k3", 3]}
2385 "db" => [":key1", ":key3", ":key2"]
2391 test "saving special atoms", %{conn: conn} do
2393 post(conn, "/api/pleroma/admin/config", %{
2396 "group" => ":pleroma",
2402 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2410 assert json_response(conn, 200) == %{
2413 "group" => ":pleroma",
2419 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2423 "db" => [":ssl_options"]
2428 assert Application.get_env(:pleroma, :key1) == [
2429 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
2433 test "saving full setting if value is in full_key_update list", %{conn: conn} do
2434 backends = Application.get_env(:logger, :backends)
2435 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
2441 value: :erlang.term_to_binary([])
2445 post(conn, "/api/pleroma/admin/config", %{
2448 group: config.group,
2450 value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}]
2455 assert json_response(conn, 200) == %{
2458 "group" => ":logger",
2459 "key" => ":backends",
2462 %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}
2464 "db" => [":backends"]
2469 assert Application.get_env(:logger, :backends) == [
2471 {ExSyslogger, :ex_syslogger}
2476 Logger.warn("Ooops...")
2480 test "saving full setting if value is not keyword", %{conn: conn} do
2485 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
2489 post(conn, "/api/pleroma/admin/config", %{
2491 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
2495 assert json_response(conn, 200) == %{
2498 "group" => ":tesla",
2499 "key" => ":adapter",
2500 "value" => "Tesla.Adapter.Httpc",
2501 "db" => [":adapter"]
2507 test "update config setting & delete with fallback to default value", %{
2512 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2513 config1 = insert(:config, key: ":keyaa1")
2514 config2 = insert(:config, key: ":keyaa2")
2518 group: ":ueberauth",
2523 post(conn, "/api/pleroma/admin/config", %{
2525 %{group: config1.group, key: config1.key, value: "another_value"},
2526 %{group: config2.group, key: config2.key, value: "another_value"}
2530 assert json_response(conn, 200) == %{
2533 "group" => ":pleroma",
2534 "key" => config1.key,
2535 "value" => "another_value",
2539 "group" => ":pleroma",
2540 "key" => config2.key,
2541 "value" => "another_value",
2547 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2548 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
2549 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
2553 |> assign(:user, admin)
2554 |> assign(:token, token)
2555 |> post("/api/pleroma/admin/config", %{
2557 %{group: config2.group, key: config2.key, delete: true},
2559 group: ":ueberauth",
2566 assert json_response(conn, 200) == %{
2570 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
2571 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
2574 test "common config example", %{conn: conn} do
2575 adapter = Application.get_env(:tesla, :adapter)
2576 on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
2579 post(conn, "/api/pleroma/admin/config", %{
2582 "group" => ":pleroma",
2583 "key" => "Pleroma.Captcha.NotReal",
2585 %{"tuple" => [":enabled", false]},
2586 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2587 %{"tuple" => [":seconds_valid", 60]},
2588 %{"tuple" => [":path", ""]},
2589 %{"tuple" => [":key1", nil]},
2590 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2591 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2592 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2593 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2594 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2595 %{"tuple" => [":name", "Pleroma"]}
2599 "group" => ":tesla",
2600 "key" => ":adapter",
2601 "value" => "Tesla.Adapter.Httpc"
2606 assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
2607 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2609 assert json_response(conn, 200) == %{
2612 "group" => ":pleroma",
2613 "key" => "Pleroma.Captcha.NotReal",
2615 %{"tuple" => [":enabled", false]},
2616 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2617 %{"tuple" => [":seconds_valid", 60]},
2618 %{"tuple" => [":path", ""]},
2619 %{"tuple" => [":key1", nil]},
2620 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2621 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2622 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2623 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2624 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2625 %{"tuple" => [":name", "Pleroma"]}
2642 "group" => ":tesla",
2643 "key" => ":adapter",
2644 "value" => "Tesla.Adapter.Httpc",
2645 "db" => [":adapter"]
2651 test "tuples with more than two values", %{conn: conn} do
2653 post(conn, "/api/pleroma/admin/config", %{
2656 "group" => ":pleroma",
2657 "key" => "Pleroma.Web.Endpoint.NotReal",
2673 "/api/v1/streaming",
2674 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2681 "Phoenix.Endpoint.CowboyWebSocket",
2684 "Phoenix.Transports.WebSocket",
2687 "Pleroma.Web.Endpoint",
2688 "Pleroma.Web.UserSocket",
2699 "Phoenix.Endpoint.Cowboy2Handler",
2700 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2717 assert json_response(conn, 200) == %{
2720 "group" => ":pleroma",
2721 "key" => "Pleroma.Web.Endpoint.NotReal",
2737 "/api/v1/streaming",
2738 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2745 "Phoenix.Endpoint.CowboyWebSocket",
2748 "Phoenix.Transports.WebSocket",
2751 "Pleroma.Web.Endpoint",
2752 "Pleroma.Web.UserSocket",
2763 "Phoenix.Endpoint.Cowboy2Handler",
2764 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2783 test "settings with nesting map", %{conn: conn} do
2785 post(conn, "/api/pleroma/admin/config", %{
2788 "group" => ":pleroma",
2791 %{"tuple" => [":key2", "some_val"]},
2796 ":max_options" => 20,
2797 ":max_option_chars" => 200,
2798 ":min_expiration" => 0,
2799 ":max_expiration" => 31_536_000,
2801 ":max_options" => 20,
2802 ":max_option_chars" => 200,
2803 ":min_expiration" => 0,
2804 ":max_expiration" => 31_536_000
2814 assert json_response(conn, 200) ==
2818 "group" => ":pleroma",
2821 %{"tuple" => [":key2", "some_val"]},
2826 ":max_expiration" => 31_536_000,
2827 ":max_option_chars" => 200,
2828 ":max_options" => 20,
2829 ":min_expiration" => 0,
2831 ":max_expiration" => 31_536_000,
2832 ":max_option_chars" => 200,
2833 ":max_options" => 20,
2834 ":min_expiration" => 0
2840 "db" => [":key2", ":key3"]
2846 test "value as map", %{conn: conn} do
2848 post(conn, "/api/pleroma/admin/config", %{
2851 "group" => ":pleroma",
2853 "value" => %{"key" => "some_val"}
2858 assert json_response(conn, 200) ==
2862 "group" => ":pleroma",
2864 "value" => %{"key" => "some_val"},
2871 test "queues key as atom", %{conn: conn} do
2873 post(conn, "/api/pleroma/admin/config", %{
2879 %{"tuple" => [":federator_incoming", 50]},
2880 %{"tuple" => [":federator_outgoing", 50]},
2881 %{"tuple" => [":web_push", 50]},
2882 %{"tuple" => [":mailer", 10]},
2883 %{"tuple" => [":transmogrifier", 20]},
2884 %{"tuple" => [":scheduled_activities", 10]},
2885 %{"tuple" => [":background", 5]}
2891 assert json_response(conn, 200) == %{
2897 %{"tuple" => [":federator_incoming", 50]},
2898 %{"tuple" => [":federator_outgoing", 50]},
2899 %{"tuple" => [":web_push", 50]},
2900 %{"tuple" => [":mailer", 10]},
2901 %{"tuple" => [":transmogrifier", 20]},
2902 %{"tuple" => [":scheduled_activities", 10]},
2903 %{"tuple" => [":background", 5]}
2906 ":federator_incoming",
2907 ":federator_outgoing",
2911 ":scheduled_activities",
2919 test "delete part of settings by atom subkeys", %{conn: conn} do
2923 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2927 post(conn, "/api/pleroma/admin/config", %{
2930 group: config.group,
2932 subkeys: [":subkey1", ":subkey3"],
2938 assert json_response(conn, 200) == %{
2941 "group" => ":pleroma",
2943 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2944 "db" => [":subkey2"]
2950 test "proxy tuple localhost", %{conn: conn} do
2952 post(conn, "/api/pleroma/admin/config", %{
2958 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2959 %{"tuple" => [":send_user_agent", false]}
2965 assert json_response(conn, 200) == %{
2968 "group" => ":pleroma",
2971 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2972 %{"tuple" => [":send_user_agent", false]}
2974 "db" => [":proxy_url", ":send_user_agent"]
2980 test "proxy tuple domain", %{conn: conn} do
2982 post(conn, "/api/pleroma/admin/config", %{
2988 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
2989 %{"tuple" => [":send_user_agent", false]}
2995 assert json_response(conn, 200) == %{
2998 "group" => ":pleroma",
3001 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
3002 %{"tuple" => [":send_user_agent", false]}
3004 "db" => [":proxy_url", ":send_user_agent"]
3010 test "proxy tuple ip", %{conn: conn} do
3012 post(conn, "/api/pleroma/admin/config", %{
3018 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
3019 %{"tuple" => [":send_user_agent", false]}
3025 assert json_response(conn, 200) == %{
3028 "group" => ":pleroma",
3031 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
3032 %{"tuple" => [":send_user_agent", false]}
3034 "db" => [":proxy_url", ":send_user_agent"]
3041 describe "GET /api/pleroma/admin/restart" do
3042 setup do: clear_config(:configurable_from_database, true)
3044 test "pleroma restarts", %{conn: conn} do
3046 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
3047 end) =~ "pleroma restarted"
3049 refute Restarter.Pleroma.need_reboot?()
3053 describe "GET /api/pleroma/admin/statuses" do
3054 test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
3055 blocked = insert(:user)
3056 user = insert(:user)
3057 User.block(admin, blocked)
3060 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
3062 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
3063 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
3064 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
3065 {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
3069 |> get("/api/pleroma/admin/statuses")
3070 |> json_response(200)
3072 refute "private" in Enum.map(response, & &1["visibility"])
3073 assert length(response) == 3
3076 test "returns only local statuses with local_only on", %{conn: conn} do
3077 user = insert(:user)
3078 remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
3079 insert(:note_activity, user: user, local: true)
3080 insert(:note_activity, user: remote_user, local: false)
3084 |> get("/api/pleroma/admin/statuses?local_only=true")
3085 |> json_response(200)
3087 assert length(response) == 1
3090 test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
3091 user = insert(:user)
3094 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
3096 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
3097 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
3098 conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
3099 assert json_response(conn, 200) |> length() == 3
3103 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
3105 user = insert(:user)
3107 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
3108 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
3109 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
3111 insert(:note_activity, user: user, published: date1)
3112 insert(:note_activity, user: user, published: date2)
3113 insert(:note_activity, user: user, published: date3)
3118 test "renders user's statuses", %{conn: conn, user: user} do
3119 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
3121 assert json_response(conn, 200) |> length() == 3
3124 test "renders user's statuses with a limit", %{conn: conn, user: user} do
3125 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
3127 assert json_response(conn, 200) |> length() == 2
3130 test "doesn't return private statuses by default", %{conn: conn, user: user} do
3131 {:ok, _private_status} =
3132 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
3134 {:ok, _public_status} =
3135 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
3137 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
3139 assert json_response(conn, 200) |> length() == 4
3142 test "returns private statuses with godmode on", %{conn: conn, user: user} do
3143 {:ok, _private_status} =
3144 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
3146 {:ok, _public_status} =
3147 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
3149 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
3151 assert json_response(conn, 200) |> length() == 5
3154 test "excludes reblogs by default", %{conn: conn, user: user} do
3155 other_user = insert(:user)
3156 {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
3157 {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
3159 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
3160 assert json_response(conn_res, 200) |> length() == 0
3163 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
3165 assert json_response(conn_res, 200) |> length() == 1
3169 describe "GET /api/pleroma/admin/moderation_log" do
3171 moderator = insert(:user, is_moderator: true)
3173 %{moderator: moderator}
3176 test "returns the log", %{conn: conn, admin: admin} do
3177 Repo.insert(%ModerationLog{
3181 "nickname" => admin.nickname,
3184 action: "relay_follow",
3185 target: "https://example.org/relay"
3187 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3190 Repo.insert(%ModerationLog{
3194 "nickname" => admin.nickname,
3197 action: "relay_unfollow",
3198 target: "https://example.org/relay"
3200 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3203 conn = get(conn, "/api/pleroma/admin/moderation_log")
3205 response = json_response(conn, 200)
3206 [first_entry, second_entry] = response["items"]
3208 assert response["total"] == 2
3209 assert first_entry["data"]["action"] == "relay_unfollow"
3211 assert first_entry["message"] ==
3212 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3214 assert second_entry["data"]["action"] == "relay_follow"
3216 assert second_entry["message"] ==
3217 "@#{admin.nickname} followed relay: https://example.org/relay"
3220 test "returns the log with pagination", %{conn: conn, admin: admin} do
3221 Repo.insert(%ModerationLog{
3225 "nickname" => admin.nickname,
3228 action: "relay_follow",
3229 target: "https://example.org/relay"
3231 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3234 Repo.insert(%ModerationLog{
3238 "nickname" => admin.nickname,
3241 action: "relay_unfollow",
3242 target: "https://example.org/relay"
3244 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3247 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3249 response1 = json_response(conn1, 200)
3250 [first_entry] = response1["items"]
3252 assert response1["total"] == 2
3253 assert response1["items"] |> length() == 1
3254 assert first_entry["data"]["action"] == "relay_unfollow"
3256 assert first_entry["message"] ==
3257 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3259 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3261 response2 = json_response(conn2, 200)
3262 [second_entry] = response2["items"]
3264 assert response2["total"] == 2
3265 assert response2["items"] |> length() == 1
3266 assert second_entry["data"]["action"] == "relay_follow"
3268 assert second_entry["message"] ==
3269 "@#{admin.nickname} followed relay: https://example.org/relay"
3272 test "filters log by date", %{conn: conn, admin: admin} do
3273 first_date = "2017-08-15T15:47:06Z"
3274 second_date = "2017-08-20T15:47:06Z"
3276 Repo.insert(%ModerationLog{
3280 "nickname" => admin.nickname,
3283 action: "relay_follow",
3284 target: "https://example.org/relay"
3286 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3289 Repo.insert(%ModerationLog{
3293 "nickname" => admin.nickname,
3296 action: "relay_unfollow",
3297 target: "https://example.org/relay"
3299 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3305 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3308 response1 = json_response(conn1, 200)
3309 [first_entry] = response1["items"]
3311 assert response1["total"] == 1
3312 assert first_entry["data"]["action"] == "relay_unfollow"
3314 assert first_entry["message"] ==
3315 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3318 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3319 Repo.insert(%ModerationLog{
3323 "nickname" => admin.nickname,
3326 action: "relay_follow",
3327 target: "https://example.org/relay"
3331 Repo.insert(%ModerationLog{
3334 "id" => moderator.id,
3335 "nickname" => moderator.nickname,
3338 action: "relay_unfollow",
3339 target: "https://example.org/relay"
3343 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3345 response1 = json_response(conn1, 200)
3346 [first_entry] = response1["items"]
3348 assert response1["total"] == 1
3349 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3352 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3353 ModerationLog.insert_log(%{
3355 action: "relay_follow",
3356 target: "https://example.org/relay"
3359 ModerationLog.insert_log(%{
3361 action: "relay_unfollow",
3362 target: "https://example.org/relay"
3365 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3367 response1 = json_response(conn1, 200)
3368 [first_entry] = response1["items"]
3370 assert response1["total"] == 1
3372 assert get_in(first_entry, ["data", "message"]) ==
3373 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3377 describe "GET /users/:nickname/credentials" do
3378 test "gets the user credentials", %{conn: conn} do
3379 user = insert(:user)
3380 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3382 response = assert json_response(conn, 200)
3383 assert response["email"] == user.email
3386 test "returns 403 if requested by a non-admin" do
3387 user = insert(:user)
3391 |> assign(:user, user)
3392 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3394 assert json_response(conn, :forbidden)
3398 describe "PATCH /users/:nickname/credentials" do
3399 test "changes password and email", %{conn: conn, admin: admin} do
3400 user = insert(:user)
3401 assert user.password_reset_pending == false
3404 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3405 "password" => "new_password",
3406 "email" => "new_email@example.com",
3407 "name" => "new_name"
3410 assert json_response(conn, 200) == %{"status" => "success"}
3412 ObanHelpers.perform_all()
3414 updated_user = User.get_by_id(user.id)
3416 assert updated_user.email == "new_email@example.com"
3417 assert updated_user.name == "new_name"
3418 assert updated_user.password_hash != user.password_hash
3419 assert updated_user.password_reset_pending == true
3421 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3423 assert ModerationLog.get_log_entry_message(log_entry1) ==
3424 "@#{admin.nickname} updated users: @#{user.nickname}"
3426 assert ModerationLog.get_log_entry_message(log_entry2) ==
3427 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3430 test "returns 403 if requested by a non-admin" do
3431 user = insert(:user)
3435 |> assign(:user, user)
3436 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3437 "password" => "new_password",
3438 "email" => "new_email@example.com",
3439 "name" => "new_name"
3442 assert json_response(conn, :forbidden)
3446 describe "PATCH /users/:nickname/force_password_reset" do
3447 test "sets password_reset_pending to true", %{conn: conn} do
3448 user = insert(:user)
3449 assert user.password_reset_pending == false
3452 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3454 assert json_response(conn, 204) == ""
3456 ObanHelpers.perform_all()
3458 assert User.get_by_id(user.id).password_reset_pending == true
3462 describe "relays" do
3463 test "POST /relay", %{conn: conn, admin: admin} do
3465 post(conn, "/api/pleroma/admin/relay", %{
3466 relay_url: "http://mastodon.example.org/users/admin"
3469 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3471 log_entry = Repo.one(ModerationLog)
3473 assert ModerationLog.get_log_entry_message(log_entry) ==
3474 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3477 test "GET /relay", %{conn: conn} do
3478 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3480 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3481 |> Enum.each(fn ap_id ->
3482 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3483 User.follow(relay_user, user)
3486 conn = get(conn, "/api/pleroma/admin/relay")
3488 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3491 test "DELETE /relay", %{conn: conn, admin: admin} do
3492 post(conn, "/api/pleroma/admin/relay", %{
3493 relay_url: "http://mastodon.example.org/users/admin"
3497 delete(conn, "/api/pleroma/admin/relay", %{
3498 relay_url: "http://mastodon.example.org/users/admin"
3501 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3503 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3505 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3506 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3508 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3509 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3513 describe "instances" do
3514 test "GET /instances/:instance/statuses", %{conn: conn} do
3515 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3516 user2 = insert(:user, local: false, nickname: "test@test.com")
3517 insert_pair(:note_activity, user: user)
3518 activity = insert(:note_activity, user: user2)
3520 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3522 response = json_response(ret_conn, 200)
3524 assert length(response) == 2
3526 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3528 response = json_response(ret_conn, 200)
3530 assert length(response) == 1
3532 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3534 response = json_response(ret_conn, 200)
3536 assert Enum.empty?(response)
3538 CommonAPI.repeat(activity.id, user)
3540 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3541 response = json_response(ret_conn, 200)
3542 assert length(response) == 2
3544 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3545 response = json_response(ret_conn, 200)
3546 assert length(response) == 3
3550 describe "PATCH /confirm_email" do
3551 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3552 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3554 assert first_user.confirmation_pending == true
3555 assert second_user.confirmation_pending == true
3558 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3560 first_user.nickname,
3561 second_user.nickname
3565 assert ret_conn.status == 200
3567 assert first_user.confirmation_pending == true
3568 assert second_user.confirmation_pending == true
3570 log_entry = Repo.one(ModerationLog)
3572 assert ModerationLog.get_log_entry_message(log_entry) ==
3573 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3574 second_user.nickname
3579 describe "PATCH /resend_confirmation_email" do
3580 test "it resend emails for two users", %{conn: conn, admin: admin} do
3581 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3584 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3586 first_user.nickname,
3587 second_user.nickname
3591 assert ret_conn.status == 200
3593 log_entry = Repo.one(ModerationLog)
3595 assert ModerationLog.get_log_entry_message(log_entry) ==
3596 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3597 second_user.nickname
3602 describe "POST /reports/:id/notes" do
3603 setup %{conn: conn, admin: admin} do
3604 [reporter, target_user] = insert_pair(:user)
3605 activity = insert(:note_activity, user: target_user)
3607 {:ok, %{id: report_id}} =
3608 CommonAPI.report(reporter, %{
3609 "account_id" => target_user.id,
3610 "comment" => "I feel offended",
3611 "status_ids" => [activity.id]
3614 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3615 content: "this is disgusting!"
3618 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3619 content: "this is disgusting2!"
3624 report_id: report_id
3628 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3629 [note, _] = Repo.all(ReportNote)
3632 activity_id: ^report_id,
3633 content: "this is disgusting!",
3638 test "it returns reports with notes", %{conn: conn, admin: admin} do
3639 conn = get(conn, "/api/pleroma/admin/reports")
3641 response = json_response(conn, 200)
3642 notes = hd(response["reports"])["notes"]
3645 assert note["user"]["nickname"] == admin.nickname
3646 assert note["content"] == "this is disgusting!"
3647 assert note["created_at"]
3648 assert response["total"] == 1
3651 test "it deletes the note", %{conn: conn, report_id: report_id} do
3652 assert ReportNote |> Repo.all() |> length() == 2
3654 [note, _] = Repo.all(ReportNote)
3656 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3658 assert ReportNote |> Repo.all() |> length() == 1
3662 test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do
3663 admin = insert(:user, is_admin: true)
3666 assign(conn, :user, admin)
3667 |> get("/api/pleroma/admin/config/descriptions")
3669 assert [child | _others] = json_response(conn, 200)
3671 assert child["children"]
3673 assert String.starts_with?(child["group"], ":")
3674 assert child["description"]
3677 describe "/api/pleroma/admin/stats" do
3678 test "status visibility count", %{conn: conn} do
3679 admin = insert(:user, is_admin: true)
3680 user = insert(:user)
3681 CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
3682 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3683 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3687 |> assign(:user, admin)
3688 |> get("/api/pleroma/admin/stats")
3689 |> json_response(200)
3691 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3692 response["status_visibility"]
3697 # Needed for testing
3698 defmodule Pleroma.Web.Endpoint.NotReal do
3701 defmodule Pleroma.Captcha.NotReal do