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 clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
47 Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
50 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
53 url = "/api/pleroma/admin/users/#{user.nickname}"
55 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
56 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
57 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
59 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
60 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
63 for good_token <- [good_token1, good_token2, good_token3] do
66 |> assign(:user, admin)
67 |> assign(:token, good_token)
70 assert json_response(conn, 200)
73 for good_token <- [good_token1, good_token2, good_token3] do
77 |> assign(:token, good_token)
80 assert json_response(conn, :forbidden)
83 for bad_token <- [bad_token1, bad_token2, bad_token3] do
86 |> assign(:user, admin)
87 |> assign(:token, bad_token)
90 assert json_response(conn, :forbidden)
95 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
96 clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
97 Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
100 test "GET /api/pleroma/admin/users/:nickname requires " <>
101 "read:accounts or admin:read:accounts or broader scope",
104 url = "/api/pleroma/admin/users/#{user.nickname}"
106 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
107 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
108 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
109 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
110 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
112 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
114 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
115 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
118 for good_token <- good_tokens do
121 |> assign(:user, admin)
122 |> assign(:token, good_token)
125 assert json_response(conn, 200)
128 for good_token <- good_tokens do
131 |> assign(:user, nil)
132 |> assign(:token, good_token)
135 assert json_response(conn, :forbidden)
138 for bad_token <- [bad_token1, bad_token2, bad_token3] do
141 |> assign(:user, admin)
142 |> assign(:token, bad_token)
145 assert json_response(conn, :forbidden)
150 describe "DELETE /api/pleroma/admin/users" do
151 test "single user", %{admin: admin, conn: conn} do
156 |> put_req_header("accept", "application/json")
157 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
159 log_entry = Repo.one(ModerationLog)
161 assert ModerationLog.get_log_entry_message(log_entry) ==
162 "@#{admin.nickname} deleted users: @#{user.nickname}"
164 assert json_response(conn, 200) == user.nickname
167 test "multiple users", %{admin: admin, conn: conn} do
168 user_one = insert(:user)
169 user_two = insert(:user)
173 |> put_req_header("accept", "application/json")
174 |> delete("/api/pleroma/admin/users", %{
175 nicknames: [user_one.nickname, user_two.nickname]
178 log_entry = Repo.one(ModerationLog)
180 assert ModerationLog.get_log_entry_message(log_entry) ==
181 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
183 response = json_response(conn, 200)
184 assert response -- [user_one.nickname, user_two.nickname] == []
188 describe "/api/pleroma/admin/users" do
189 test "Create", %{conn: conn} do
192 |> put_req_header("accept", "application/json")
193 |> post("/api/pleroma/admin/users", %{
196 "nickname" => "lain",
197 "email" => "lain@example.org",
201 "nickname" => "lain2",
202 "email" => "lain2@example.org",
208 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
209 assert response == ["success", "success"]
211 log_entry = Repo.one(ModerationLog)
213 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
216 test "Cannot create user with existing email", %{conn: conn} do
221 |> put_req_header("accept", "application/json")
222 |> post("/api/pleroma/admin/users", %{
225 "nickname" => "lain",
226 "email" => user.email,
232 assert json_response(conn, 409) == [
236 "email" => user.email,
239 "error" => "email has already been taken",
245 test "Cannot create user with existing nickname", %{conn: conn} do
250 |> put_req_header("accept", "application/json")
251 |> post("/api/pleroma/admin/users", %{
254 "nickname" => user.nickname,
255 "email" => "someuser@plerama.social",
261 assert json_response(conn, 409) == [
265 "email" => "someuser@plerama.social",
266 "nickname" => user.nickname
268 "error" => "nickname has already been taken",
274 test "Multiple user creation works in transaction", %{conn: conn} do
279 |> put_req_header("accept", "application/json")
280 |> post("/api/pleroma/admin/users", %{
283 "nickname" => "newuser",
284 "email" => "newuser@pleroma.social",
288 "nickname" => "lain",
289 "email" => user.email,
295 assert json_response(conn, 409) == [
299 "email" => user.email,
302 "error" => "email has already been taken",
308 "email" => "newuser@pleroma.social",
309 "nickname" => "newuser"
316 assert User.get_by_nickname("newuser") === nil
320 describe "/api/pleroma/admin/users/:nickname" do
321 test "Show", %{conn: conn} do
324 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
327 "deactivated" => false,
328 "id" => to_string(user.id),
330 "nickname" => user.nickname,
331 "roles" => %{"admin" => false, "moderator" => false},
333 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
334 "display_name" => HTML.strip_tags(user.name || user.nickname),
335 "confirmation_pending" => false
338 assert expected == json_response(conn, 200)
341 test "when the user doesn't exist", %{conn: conn} do
344 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
346 assert "Not found" == json_response(conn, 404)
350 describe "/api/pleroma/admin/users/follow" do
351 test "allows to force-follow another user", %{admin: admin, conn: conn} do
353 follower = insert(:user)
356 |> put_req_header("accept", "application/json")
357 |> post("/api/pleroma/admin/users/follow", %{
358 "follower" => follower.nickname,
359 "followed" => user.nickname
362 user = User.get_cached_by_id(user.id)
363 follower = User.get_cached_by_id(follower.id)
365 assert User.following?(follower, user)
367 log_entry = Repo.one(ModerationLog)
369 assert ModerationLog.get_log_entry_message(log_entry) ==
370 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
374 describe "/api/pleroma/admin/users/unfollow" do
375 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
377 follower = insert(:user)
379 User.follow(follower, user)
382 |> put_req_header("accept", "application/json")
383 |> post("/api/pleroma/admin/users/unfollow", %{
384 "follower" => follower.nickname,
385 "followed" => user.nickname
388 user = User.get_cached_by_id(user.id)
389 follower = User.get_cached_by_id(follower.id)
391 refute User.following?(follower, user)
393 log_entry = Repo.one(ModerationLog)
395 assert ModerationLog.get_log_entry_message(log_entry) ==
396 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
400 describe "PUT /api/pleroma/admin/users/tag" do
401 setup %{conn: conn} do
402 user1 = insert(:user, %{tags: ["x"]})
403 user2 = insert(:user, %{tags: ["y"]})
404 user3 = insert(:user, %{tags: ["unchanged"]})
408 |> put_req_header("accept", "application/json")
410 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
411 "#{user2.nickname}&tags[]=foo&tags[]=bar"
414 %{conn: conn, user1: user1, user2: user2, user3: user3}
417 test "it appends specified tags to users with specified nicknames", %{
423 assert json_response(conn, :no_content)
424 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
425 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
427 log_entry = Repo.one(ModerationLog)
430 [user1.nickname, user2.nickname]
431 |> Enum.map(&"@#{&1}")
434 tags = ["foo", "bar"] |> Enum.join(", ")
436 assert ModerationLog.get_log_entry_message(log_entry) ==
437 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
440 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
441 assert json_response(conn, :no_content)
442 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
446 describe "DELETE /api/pleroma/admin/users/tag" do
447 setup %{conn: conn} do
448 user1 = insert(:user, %{tags: ["x"]})
449 user2 = insert(:user, %{tags: ["y", "z"]})
450 user3 = insert(:user, %{tags: ["unchanged"]})
454 |> put_req_header("accept", "application/json")
456 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
457 "#{user2.nickname}&tags[]=x&tags[]=z"
460 %{conn: conn, user1: user1, user2: user2, user3: user3}
463 test "it removes specified tags from users with specified nicknames", %{
469 assert json_response(conn, :no_content)
470 assert User.get_cached_by_id(user1.id).tags == []
471 assert User.get_cached_by_id(user2.id).tags == ["y"]
473 log_entry = Repo.one(ModerationLog)
476 [user1.nickname, user2.nickname]
477 |> Enum.map(&"@#{&1}")
480 tags = ["x", "z"] |> Enum.join(", ")
482 assert ModerationLog.get_log_entry_message(log_entry) ==
483 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
486 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
487 assert json_response(conn, :no_content)
488 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
492 describe "/api/pleroma/admin/users/:nickname/permission_group" do
493 test "GET is giving user_info", %{admin: admin, conn: conn} do
496 |> put_req_header("accept", "application/json")
497 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
499 assert json_response(conn, 200) == %{
501 "is_moderator" => false
505 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
510 |> put_req_header("accept", "application/json")
511 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
513 assert json_response(conn, 200) == %{
517 log_entry = Repo.one(ModerationLog)
519 assert ModerationLog.get_log_entry_message(log_entry) ==
520 "@#{admin.nickname} made @#{user.nickname} admin"
523 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
524 user_one = insert(:user)
525 user_two = insert(:user)
529 |> put_req_header("accept", "application/json")
530 |> post("/api/pleroma/admin/users/permission_group/admin", %{
531 nicknames: [user_one.nickname, user_two.nickname]
534 assert json_response(conn, 200) == %{"is_admin" => true}
536 log_entry = Repo.one(ModerationLog)
538 assert ModerationLog.get_log_entry_message(log_entry) ==
539 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
542 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
543 user = insert(:user, is_admin: true)
547 |> put_req_header("accept", "application/json")
548 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
550 assert json_response(conn, 200) == %{"is_admin" => false}
552 log_entry = Repo.one(ModerationLog)
554 assert ModerationLog.get_log_entry_message(log_entry) ==
555 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
558 test "/:right DELETE, can remove from a permission group (multiple)", %{
562 user_one = insert(:user, is_admin: true)
563 user_two = insert(:user, is_admin: true)
567 |> put_req_header("accept", "application/json")
568 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
569 nicknames: [user_one.nickname, user_two.nickname]
572 assert json_response(conn, 200) == %{"is_admin" => false}
574 log_entry = Repo.one(ModerationLog)
576 assert ModerationLog.get_log_entry_message(log_entry) ==
577 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
583 describe "POST /api/pleroma/admin/email_invite, with valid config" do
584 clear_config([:instance, :registrations_open]) do
585 Config.put([:instance, :registrations_open], false)
588 clear_config([:instance, :invites_enabled]) do
589 Config.put([:instance, :invites_enabled], true)
592 test "sends invitation and returns 204", %{admin: admin, conn: conn} do
593 recipient_email = "foo@bar.com"
594 recipient_name = "J. D."
599 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
602 assert json_response(conn, :no_content)
604 token_record = List.last(Repo.all(Pleroma.UserInviteToken))
606 refute token_record.used
608 notify_email = Config.get([:instance, :notify_email])
609 instance_name = Config.get([:instance, :name])
612 Pleroma.Emails.UserEmail.user_invitation_email(
619 Swoosh.TestAssertions.assert_email_sent(
620 from: {instance_name, notify_email},
621 to: {recipient_name, recipient_email},
622 html_body: email.html_body
626 test "it returns 403 if requested by a non-admin" do
627 non_admin_user = insert(:user)
628 token = insert(:oauth_token, user: non_admin_user)
632 |> assign(:user, non_admin_user)
633 |> assign(:token, token)
634 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
636 assert json_response(conn, :forbidden)
640 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
641 clear_config([:instance, :registrations_open])
642 clear_config([:instance, :invites_enabled])
644 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
645 Config.put([:instance, :registrations_open], false)
646 Config.put([:instance, :invites_enabled], false)
648 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
650 assert json_response(conn, :internal_server_error)
653 test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
654 Config.put([:instance, :registrations_open], true)
655 Config.put([:instance, :invites_enabled], true)
657 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
659 assert json_response(conn, :internal_server_error)
663 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
668 |> put_req_header("accept", "application/json")
669 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
671 resp = json_response(conn, 200)
673 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
676 describe "GET /api/pleroma/admin/users" do
677 test "renders users array for the first page", %{conn: conn, admin: admin} do
678 user = insert(:user, local: false, tags: ["foo", "bar"])
679 conn = get(conn, "/api/pleroma/admin/users?page=1")
684 "deactivated" => admin.deactivated,
686 "nickname" => admin.nickname,
687 "roles" => %{"admin" => true, "moderator" => false},
690 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
691 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
692 "confirmation_pending" => false
695 "deactivated" => user.deactivated,
697 "nickname" => user.nickname,
698 "roles" => %{"admin" => false, "moderator" => false},
700 "tags" => ["foo", "bar"],
701 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
702 "display_name" => HTML.strip_tags(user.name || user.nickname),
703 "confirmation_pending" => false
706 |> Enum.sort_by(& &1["nickname"])
708 assert json_response(conn, 200) == %{
715 test "renders empty array for the second page", %{conn: conn} do
718 conn = get(conn, "/api/pleroma/admin/users?page=2")
720 assert json_response(conn, 200) == %{
727 test "regular search", %{conn: conn} do
728 user = insert(:user, nickname: "bob")
730 conn = get(conn, "/api/pleroma/admin/users?query=bo")
732 assert json_response(conn, 200) == %{
737 "deactivated" => user.deactivated,
739 "nickname" => user.nickname,
740 "roles" => %{"admin" => false, "moderator" => false},
743 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
744 "display_name" => HTML.strip_tags(user.name || user.nickname),
745 "confirmation_pending" => false
751 test "search by domain", %{conn: conn} do
752 user = insert(:user, nickname: "nickname@domain.com")
755 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
757 assert json_response(conn, 200) == %{
762 "deactivated" => user.deactivated,
764 "nickname" => user.nickname,
765 "roles" => %{"admin" => false, "moderator" => false},
768 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
769 "display_name" => HTML.strip_tags(user.name || user.nickname),
770 "confirmation_pending" => false
776 test "search by full nickname", %{conn: conn} do
777 user = insert(:user, nickname: "nickname@domain.com")
780 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
782 assert json_response(conn, 200) == %{
787 "deactivated" => user.deactivated,
789 "nickname" => user.nickname,
790 "roles" => %{"admin" => false, "moderator" => false},
793 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
794 "display_name" => HTML.strip_tags(user.name || user.nickname),
795 "confirmation_pending" => false
801 test "search by display name", %{conn: conn} do
802 user = insert(:user, name: "Display name")
805 conn = get(conn, "/api/pleroma/admin/users?name=display")
807 assert json_response(conn, 200) == %{
812 "deactivated" => user.deactivated,
814 "nickname" => user.nickname,
815 "roles" => %{"admin" => false, "moderator" => false},
818 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
819 "display_name" => HTML.strip_tags(user.name || user.nickname),
820 "confirmation_pending" => false
826 test "search by email", %{conn: conn} do
827 user = insert(:user, email: "email@example.com")
830 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
832 assert json_response(conn, 200) == %{
837 "deactivated" => user.deactivated,
839 "nickname" => user.nickname,
840 "roles" => %{"admin" => false, "moderator" => false},
843 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
844 "display_name" => HTML.strip_tags(user.name || user.nickname),
845 "confirmation_pending" => false
851 test "regular search with page size", %{conn: conn} do
852 user = insert(:user, nickname: "aalice")
853 user2 = insert(:user, nickname: "alice")
855 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
857 assert json_response(conn1, 200) == %{
862 "deactivated" => user.deactivated,
864 "nickname" => user.nickname,
865 "roles" => %{"admin" => false, "moderator" => false},
868 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
869 "display_name" => HTML.strip_tags(user.name || user.nickname),
870 "confirmation_pending" => false
875 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
877 assert json_response(conn2, 200) == %{
882 "deactivated" => user2.deactivated,
884 "nickname" => user2.nickname,
885 "roles" => %{"admin" => false, "moderator" => false},
888 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
889 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
890 "confirmation_pending" => false
896 test "only local users" do
897 admin = insert(:user, is_admin: true, nickname: "john")
898 token = insert(:oauth_admin_token, user: admin)
899 user = insert(:user, nickname: "bob")
901 insert(:user, nickname: "bobb", local: false)
905 |> assign(:user, admin)
906 |> assign(:token, token)
907 |> get("/api/pleroma/admin/users?query=bo&filters=local")
909 assert json_response(conn, 200) == %{
914 "deactivated" => user.deactivated,
916 "nickname" => user.nickname,
917 "roles" => %{"admin" => false, "moderator" => false},
920 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
921 "display_name" => HTML.strip_tags(user.name || user.nickname),
922 "confirmation_pending" => false
928 test "only local users with no query", %{conn: conn, admin: old_admin} do
929 admin = insert(:user, is_admin: true, nickname: "john")
930 user = insert(:user, nickname: "bob")
932 insert(:user, nickname: "bobb", local: false)
934 conn = get(conn, "/api/pleroma/admin/users?filters=local")
939 "deactivated" => user.deactivated,
941 "nickname" => user.nickname,
942 "roles" => %{"admin" => false, "moderator" => false},
945 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
946 "display_name" => HTML.strip_tags(user.name || user.nickname),
947 "confirmation_pending" => false
950 "deactivated" => admin.deactivated,
952 "nickname" => admin.nickname,
953 "roles" => %{"admin" => true, "moderator" => false},
956 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
957 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
958 "confirmation_pending" => false
961 "deactivated" => false,
962 "id" => old_admin.id,
964 "nickname" => old_admin.nickname,
965 "roles" => %{"admin" => true, "moderator" => false},
967 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
968 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
969 "confirmation_pending" => false
972 |> Enum.sort_by(& &1["nickname"])
974 assert json_response(conn, 200) == %{
981 test "load only admins", %{conn: conn, admin: admin} do
982 second_admin = insert(:user, is_admin: true)
986 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
991 "deactivated" => false,
993 "nickname" => admin.nickname,
994 "roles" => %{"admin" => true, "moderator" => false},
995 "local" => admin.local,
997 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
998 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
999 "confirmation_pending" => false
1002 "deactivated" => false,
1003 "id" => second_admin.id,
1004 "nickname" => second_admin.nickname,
1005 "roles" => %{"admin" => true, "moderator" => false},
1006 "local" => second_admin.local,
1008 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1009 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1010 "confirmation_pending" => false
1013 |> Enum.sort_by(& &1["nickname"])
1015 assert json_response(conn, 200) == %{
1022 test "load only moderators", %{conn: conn} do
1023 moderator = insert(:user, is_moderator: true)
1027 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1029 assert json_response(conn, 200) == %{
1034 "deactivated" => false,
1035 "id" => moderator.id,
1036 "nickname" => moderator.nickname,
1037 "roles" => %{"admin" => false, "moderator" => true},
1038 "local" => moderator.local,
1040 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1041 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1042 "confirmation_pending" => false
1048 test "load users with tags list", %{conn: conn} do
1049 user1 = insert(:user, tags: ["first"])
1050 user2 = insert(:user, tags: ["second"])
1054 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1059 "deactivated" => false,
1061 "nickname" => user1.nickname,
1062 "roles" => %{"admin" => false, "moderator" => false},
1063 "local" => user1.local,
1064 "tags" => ["first"],
1065 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1066 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1067 "confirmation_pending" => false
1070 "deactivated" => false,
1072 "nickname" => user2.nickname,
1073 "roles" => %{"admin" => false, "moderator" => false},
1074 "local" => user2.local,
1075 "tags" => ["second"],
1076 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1077 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1078 "confirmation_pending" => false
1081 |> Enum.sort_by(& &1["nickname"])
1083 assert json_response(conn, 200) == %{
1090 test "it works with multiple filters" do
1091 admin = insert(:user, nickname: "john", is_admin: true)
1092 token = insert(:oauth_admin_token, user: admin)
1093 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1095 insert(:user, nickname: "ken", local: true, deactivated: true)
1096 insert(:user, nickname: "bobb", local: false, deactivated: false)
1100 |> assign(:user, admin)
1101 |> assign(:token, token)
1102 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1104 assert json_response(conn, 200) == %{
1109 "deactivated" => user.deactivated,
1111 "nickname" => user.nickname,
1112 "roles" => %{"admin" => false, "moderator" => false},
1113 "local" => user.local,
1115 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1116 "display_name" => HTML.strip_tags(user.name || user.nickname),
1117 "confirmation_pending" => false
1123 test "it omits relay user", %{admin: admin, conn: conn} do
1124 assert %User{} = Relay.get_actor()
1126 conn = get(conn, "/api/pleroma/admin/users")
1128 assert json_response(conn, 200) == %{
1133 "deactivated" => admin.deactivated,
1135 "nickname" => admin.nickname,
1136 "roles" => %{"admin" => true, "moderator" => false},
1139 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1140 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1141 "confirmation_pending" => false
1148 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1149 user_one = insert(:user, deactivated: true)
1150 user_two = insert(:user, deactivated: true)
1155 "/api/pleroma/admin/users/activate",
1156 %{nicknames: [user_one.nickname, user_two.nickname]}
1159 response = json_response(conn, 200)
1160 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1162 log_entry = Repo.one(ModerationLog)
1164 assert ModerationLog.get_log_entry_message(log_entry) ==
1165 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1168 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1169 user_one = insert(:user, deactivated: false)
1170 user_two = insert(:user, deactivated: false)
1175 "/api/pleroma/admin/users/deactivate",
1176 %{nicknames: [user_one.nickname, user_two.nickname]}
1179 response = json_response(conn, 200)
1180 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1182 log_entry = Repo.one(ModerationLog)
1184 assert ModerationLog.get_log_entry_message(log_entry) ==
1185 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1188 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1189 user = insert(:user)
1191 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1193 assert json_response(conn, 200) ==
1195 "deactivated" => !user.deactivated,
1197 "nickname" => user.nickname,
1198 "roles" => %{"admin" => false, "moderator" => false},
1201 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1202 "display_name" => HTML.strip_tags(user.name || user.nickname),
1203 "confirmation_pending" => false
1206 log_entry = Repo.one(ModerationLog)
1208 assert ModerationLog.get_log_entry_message(log_entry) ==
1209 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1212 describe "POST /api/pleroma/admin/users/invite_token" do
1213 test "without options", %{conn: conn} do
1214 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1216 invite_json = json_response(conn, 200)
1217 invite = UserInviteToken.find_by_token!(invite_json["token"])
1219 refute invite.expires_at
1220 refute invite.max_use
1221 assert invite.invite_type == "one_time"
1224 test "with expires_at", %{conn: conn} do
1226 post(conn, "/api/pleroma/admin/users/invite_token", %{
1227 "expires_at" => Date.to_string(Date.utc_today())
1230 invite_json = json_response(conn, 200)
1231 invite = UserInviteToken.find_by_token!(invite_json["token"])
1234 assert invite.expires_at == Date.utc_today()
1235 refute invite.max_use
1236 assert invite.invite_type == "date_limited"
1239 test "with max_use", %{conn: conn} do
1240 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1242 invite_json = json_response(conn, 200)
1243 invite = UserInviteToken.find_by_token!(invite_json["token"])
1245 refute invite.expires_at
1246 assert invite.max_use == 150
1247 assert invite.invite_type == "reusable"
1250 test "with max use and expires_at", %{conn: conn} do
1252 post(conn, "/api/pleroma/admin/users/invite_token", %{
1254 "expires_at" => Date.to_string(Date.utc_today())
1257 invite_json = json_response(conn, 200)
1258 invite = UserInviteToken.find_by_token!(invite_json["token"])
1260 assert invite.expires_at == Date.utc_today()
1261 assert invite.max_use == 150
1262 assert invite.invite_type == "reusable_date_limited"
1266 describe "GET /api/pleroma/admin/users/invites" do
1267 test "no invites", %{conn: conn} do
1268 conn = get(conn, "/api/pleroma/admin/users/invites")
1270 assert json_response(conn, 200) == %{"invites" => []}
1273 test "with invite", %{conn: conn} do
1274 {:ok, invite} = UserInviteToken.create_invite()
1276 conn = get(conn, "/api/pleroma/admin/users/invites")
1278 assert json_response(conn, 200) == %{
1281 "expires_at" => nil,
1283 "invite_type" => "one_time",
1285 "token" => invite.token,
1294 describe "POST /api/pleroma/admin/users/revoke_invite" do
1295 test "with token", %{conn: conn} do
1296 {:ok, invite} = UserInviteToken.create_invite()
1298 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1300 assert json_response(conn, 200) == %{
1301 "expires_at" => nil,
1303 "invite_type" => "one_time",
1305 "token" => invite.token,
1311 test "with invalid token", %{conn: conn} do
1312 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1314 assert json_response(conn, :not_found) == "Not found"
1318 describe "GET /api/pleroma/admin/reports/:id" do
1319 test "returns report by its id", %{conn: conn} do
1320 [reporter, target_user] = insert_pair(:user)
1321 activity = insert(:note_activity, user: target_user)
1323 {:ok, %{id: report_id}} =
1324 CommonAPI.report(reporter, %{
1325 "account_id" => target_user.id,
1326 "comment" => "I feel offended",
1327 "status_ids" => [activity.id]
1332 |> get("/api/pleroma/admin/reports/#{report_id}")
1333 |> json_response(:ok)
1335 assert response["id"] == report_id
1338 test "returns 404 when report id is invalid", %{conn: conn} do
1339 conn = get(conn, "/api/pleroma/admin/reports/test")
1341 assert json_response(conn, :not_found) == "Not found"
1345 describe "PATCH /api/pleroma/admin/reports" do
1347 [reporter, target_user] = insert_pair(:user)
1348 activity = insert(:note_activity, user: target_user)
1350 {:ok, %{id: report_id}} =
1351 CommonAPI.report(reporter, %{
1352 "account_id" => target_user.id,
1353 "comment" => "I feel offended",
1354 "status_ids" => [activity.id]
1357 {:ok, %{id: second_report_id}} =
1358 CommonAPI.report(reporter, %{
1359 "account_id" => target_user.id,
1360 "comment" => "I feel very offended",
1361 "status_ids" => [activity.id]
1366 second_report_id: second_report_id
1370 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1371 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1372 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1376 |> assign(:token, read_token)
1377 |> patch("/api/pleroma/admin/reports", %{
1378 "reports" => [%{"state" => "resolved", "id" => id}]
1380 |> json_response(403)
1382 assert response == %{
1383 "error" => "Insufficient permissions: admin:write:reports."
1387 |> assign(:token, write_token)
1388 |> patch("/api/pleroma/admin/reports", %{
1389 "reports" => [%{"state" => "resolved", "id" => id}]
1391 |> json_response(:no_content)
1394 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1396 |> patch("/api/pleroma/admin/reports", %{
1398 %{"state" => "resolved", "id" => id}
1401 |> json_response(:no_content)
1403 activity = Activity.get_by_id(id)
1404 assert activity.data["state"] == "resolved"
1406 log_entry = Repo.one(ModerationLog)
1408 assert ModerationLog.get_log_entry_message(log_entry) ==
1409 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1412 test "closes report", %{conn: conn, id: id, admin: admin} do
1414 |> patch("/api/pleroma/admin/reports", %{
1416 %{"state" => "closed", "id" => id}
1419 |> json_response(:no_content)
1421 activity = Activity.get_by_id(id)
1422 assert activity.data["state"] == "closed"
1424 log_entry = Repo.one(ModerationLog)
1426 assert ModerationLog.get_log_entry_message(log_entry) ==
1427 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1430 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1433 |> patch("/api/pleroma/admin/reports", %{
1435 %{"state" => "test", "id" => id}
1439 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1442 test "returns 404 when report is not exist", %{conn: conn} do
1445 |> patch("/api/pleroma/admin/reports", %{
1447 %{"state" => "closed", "id" => "test"}
1451 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1454 test "updates state of multiple reports", %{
1458 second_report_id: second_report_id
1461 |> patch("/api/pleroma/admin/reports", %{
1463 %{"state" => "resolved", "id" => id},
1464 %{"state" => "closed", "id" => second_report_id}
1467 |> json_response(:no_content)
1469 activity = Activity.get_by_id(id)
1470 second_activity = Activity.get_by_id(second_report_id)
1471 assert activity.data["state"] == "resolved"
1472 assert second_activity.data["state"] == "closed"
1474 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1476 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1477 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1479 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1480 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1484 describe "GET /api/pleroma/admin/reports" do
1485 test "returns empty response when no reports created", %{conn: conn} do
1488 |> get("/api/pleroma/admin/reports")
1489 |> json_response(:ok)
1491 assert Enum.empty?(response["reports"])
1492 assert response["total"] == 0
1495 test "returns reports", %{conn: conn} do
1496 [reporter, target_user] = insert_pair(:user)
1497 activity = insert(:note_activity, user: target_user)
1499 {:ok, %{id: report_id}} =
1500 CommonAPI.report(reporter, %{
1501 "account_id" => target_user.id,
1502 "comment" => "I feel offended",
1503 "status_ids" => [activity.id]
1508 |> get("/api/pleroma/admin/reports")
1509 |> json_response(:ok)
1511 [report] = response["reports"]
1513 assert length(response["reports"]) == 1
1514 assert report["id"] == report_id
1516 assert response["total"] == 1
1519 test "returns reports with specified state", %{conn: conn} do
1520 [reporter, target_user] = insert_pair(:user)
1521 activity = insert(:note_activity, user: target_user)
1523 {:ok, %{id: first_report_id}} =
1524 CommonAPI.report(reporter, %{
1525 "account_id" => target_user.id,
1526 "comment" => "I feel offended",
1527 "status_ids" => [activity.id]
1530 {:ok, %{id: second_report_id}} =
1531 CommonAPI.report(reporter, %{
1532 "account_id" => target_user.id,
1533 "comment" => "I don't like this user"
1536 CommonAPI.update_report_state(second_report_id, "closed")
1540 |> get("/api/pleroma/admin/reports", %{
1543 |> json_response(:ok)
1545 [open_report] = response["reports"]
1547 assert length(response["reports"]) == 1
1548 assert open_report["id"] == first_report_id
1550 assert response["total"] == 1
1554 |> get("/api/pleroma/admin/reports", %{
1557 |> json_response(:ok)
1559 [closed_report] = response["reports"]
1561 assert length(response["reports"]) == 1
1562 assert closed_report["id"] == second_report_id
1564 assert response["total"] == 1
1568 |> get("/api/pleroma/admin/reports", %{
1569 "state" => "resolved"
1571 |> json_response(:ok)
1573 assert Enum.empty?(response["reports"])
1574 assert response["total"] == 0
1577 test "returns 403 when requested by a non-admin" do
1578 user = insert(:user)
1579 token = insert(:oauth_token, user: user)
1583 |> assign(:user, user)
1584 |> assign(:token, token)
1585 |> get("/api/pleroma/admin/reports")
1587 assert json_response(conn, :forbidden) ==
1588 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1591 test "returns 403 when requested by anonymous" do
1592 conn = get(build_conn(), "/api/pleroma/admin/reports")
1594 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1598 describe "GET /api/pleroma/admin/grouped_reports" do
1600 [reporter, target_user] = insert_pair(:user)
1602 date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1603 date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1604 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1607 insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
1610 insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
1613 insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
1615 {:ok, first_report} =
1616 CommonAPI.report(reporter, %{
1617 "account_id" => target_user.id,
1618 "status_ids" => [first_status.id, second_status.id, third_status.id]
1621 {:ok, second_report} =
1622 CommonAPI.report(reporter, %{
1623 "account_id" => target_user.id,
1624 "status_ids" => [first_status.id, second_status.id]
1627 {:ok, third_report} =
1628 CommonAPI.report(reporter, %{
1629 "account_id" => target_user.id,
1630 "status_ids" => [first_status.id]
1634 first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
1635 second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
1636 third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
1637 first_report: first_report,
1638 first_status_reports: [first_report, second_report, third_report],
1639 second_status_reports: [first_report, second_report],
1640 third_status_reports: [first_report],
1641 target_user: target_user,
1646 test "returns reports grouped by status", %{
1648 first_status: first_status,
1649 second_status: second_status,
1650 third_status: third_status,
1651 first_status_reports: first_status_reports,
1652 second_status_reports: second_status_reports,
1653 third_status_reports: third_status_reports,
1654 target_user: target_user,
1659 |> get("/api/pleroma/admin/grouped_reports")
1660 |> json_response(:ok)
1662 assert length(response["reports"]) == 3
1664 first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
1666 second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id))
1668 third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id))
1670 assert length(first_group["reports"]) == 3
1671 assert length(second_group["reports"]) == 2
1672 assert length(third_group["reports"]) == 1
1674 assert first_group["date"] ==
1675 Enum.max_by(first_status_reports, fn act ->
1676 NaiveDateTime.from_iso8601!(act.data["published"])
1677 end).data["published"]
1679 assert first_group["status"] ==
1681 stringify_keys(StatusView.render("show.json", %{activity: first_status})),
1686 assert(first_group["account"]["id"] == target_user.id)
1688 assert length(first_group["actors"]) == 1
1689 assert hd(first_group["actors"])["id"] == reporter.id
1691 assert Enum.map(first_group["reports"], & &1["id"]) --
1692 Enum.map(first_status_reports, & &1.id) == []
1694 assert second_group["date"] ==
1695 Enum.max_by(second_status_reports, fn act ->
1696 NaiveDateTime.from_iso8601!(act.data["published"])
1697 end).data["published"]
1699 assert second_group["status"] ==
1701 stringify_keys(StatusView.render("show.json", %{activity: second_status})),
1706 assert second_group["account"]["id"] == target_user.id
1708 assert length(second_group["actors"]) == 1
1709 assert hd(second_group["actors"])["id"] == reporter.id
1711 assert Enum.map(second_group["reports"], & &1["id"]) --
1712 Enum.map(second_status_reports, & &1.id) == []
1714 assert third_group["date"] ==
1715 Enum.max_by(third_status_reports, fn act ->
1716 NaiveDateTime.from_iso8601!(act.data["published"])
1717 end).data["published"]
1719 assert third_group["status"] ==
1721 stringify_keys(StatusView.render("show.json", %{activity: third_status})),
1726 assert third_group["account"]["id"] == target_user.id
1728 assert length(third_group["actors"]) == 1
1729 assert hd(third_group["actors"])["id"] == reporter.id
1731 assert Enum.map(third_group["reports"], & &1["id"]) --
1732 Enum.map(third_status_reports, & &1.id) == []
1735 test "reopened report renders status data", %{
1737 first_report: first_report,
1738 first_status: first_status
1740 {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
1744 |> get("/api/pleroma/admin/grouped_reports")
1745 |> json_response(:ok)
1747 first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
1749 assert first_group["status"] ==
1751 stringify_keys(StatusView.render("show.json", %{activity: first_status})),
1757 test "reopened report does not render status data if status has been deleted", %{
1759 first_report: first_report,
1760 first_status: first_status,
1761 target_user: target_user
1763 {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
1764 {:ok, _} = CommonAPI.delete(first_status.id, target_user)
1766 refute Activity.get_by_ap_id(first_status.id)
1770 |> get("/api/pleroma/admin/grouped_reports")
1771 |> json_response(:ok)
1773 assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["status"][
1777 assert length(Enum.filter(response["reports"], &(&1["status"]["deleted"] == false))) == 2
1780 test "account not empty if status was deleted", %{
1782 first_report: first_report,
1783 first_status: first_status,
1784 target_user: target_user
1786 {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
1787 {:ok, _} = CommonAPI.delete(first_status.id, target_user)
1789 refute Activity.get_by_ap_id(first_status.id)
1793 |> get("/api/pleroma/admin/grouped_reports")
1794 |> json_response(:ok)
1796 assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["account"]
1800 describe "PUT /api/pleroma/admin/statuses/:id" do
1802 activity = insert(:note_activity)
1807 test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
1810 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
1811 |> json_response(:ok)
1813 assert response["sensitive"]
1815 log_entry = Repo.one(ModerationLog)
1817 assert ModerationLog.get_log_entry_message(log_entry) ==
1818 "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
1822 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
1823 |> json_response(:ok)
1825 refute response["sensitive"]
1828 test "change visibility flag", %{conn: conn, id: id, admin: admin} do
1831 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
1832 |> json_response(:ok)
1834 assert response["visibility"] == "public"
1836 log_entry = Repo.one(ModerationLog)
1838 assert ModerationLog.get_log_entry_message(log_entry) ==
1839 "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
1843 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
1844 |> json_response(:ok)
1846 assert response["visibility"] == "private"
1850 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
1851 |> json_response(:ok)
1853 assert response["visibility"] == "unlisted"
1856 test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
1857 conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})
1859 assert json_response(conn, :bad_request) == "Unsupported visibility"
1863 describe "DELETE /api/pleroma/admin/statuses/:id" do
1865 activity = insert(:note_activity)
1870 test "deletes status", %{conn: conn, id: id, admin: admin} do
1872 |> delete("/api/pleroma/admin/statuses/#{id}")
1873 |> json_response(:ok)
1875 refute Activity.get_by_id(id)
1877 log_entry = Repo.one(ModerationLog)
1879 assert ModerationLog.get_log_entry_message(log_entry) ==
1880 "@#{admin.nickname} deleted status ##{id}"
1883 test "returns 404 when the status does not exist", %{conn: conn} do
1884 conn = delete(conn, "/api/pleroma/admin/statuses/test")
1886 assert json_response(conn, :not_found) == "Not found"
1890 describe "GET /api/pleroma/admin/config" do
1891 clear_config(:configurable_from_database) do
1892 Config.put(:configurable_from_database, true)
1895 test "when configuration from database is off", %{conn: conn} do
1896 Config.put(:configurable_from_database, false)
1897 conn = get(conn, "/api/pleroma/admin/config")
1899 assert json_response(conn, 400) ==
1900 "To use this endpoint you need to enable configuration from database."
1903 test "with settings only in db", %{conn: conn} do
1904 config1 = insert(:config)
1905 config2 = insert(:config)
1907 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1912 "group" => ":pleroma",
1917 "group" => ":pleroma",
1922 } = json_response(conn, 200)
1924 assert key1 == config1.key
1925 assert key2 == config2.key
1928 test "db is added to settings that are in db", %{conn: conn} do
1929 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1931 %{"configs" => configs} =
1933 |> get("/api/pleroma/admin/config")
1934 |> json_response(200)
1937 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1938 group == ":pleroma" and key == ":instance"
1941 assert instance_config["db"] == [":name"]
1944 test "merged default setting with db settings", %{conn: conn} do
1945 config1 = insert(:config)
1946 config2 = insert(:config)
1950 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1953 %{"configs" => configs} =
1955 |> get("/api/pleroma/admin/config")
1956 |> json_response(200)
1958 assert length(configs) > 3
1961 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1962 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1965 assert length(received_configs) == 3
1969 |> ConfigDB.from_binary()
1971 |> ConfigDB.convert()
1973 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1974 assert db in [[config1.key], [config2.key], db_keys]
1977 ConfigDB.from_binary_with_convert(config1.value),
1978 ConfigDB.from_binary_with_convert(config2.value),
1979 ConfigDB.from_binary_with_convert(config3.value)
1984 test "subkeys with full update right merge", %{conn: conn} do
1988 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1994 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1997 %{"configs" => configs} =
1999 |> get("/api/pleroma/admin/config")
2000 |> json_response(200)
2003 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
2004 group == ":pleroma" and key in [config1.key, config2.key]
2007 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
2008 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
2010 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
2011 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
2013 assert emoji_val[:groups] == [a: 1, b: 2]
2014 assert assets_val[:mascots] == [a: 1, b: 2]
2018 test "POST /api/pleroma/admin/config error", %{conn: conn} do
2019 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
2021 assert json_response(conn, 400) ==
2022 "To use this endpoint you need to enable configuration from database."
2025 describe "POST /api/pleroma/admin/config" do
2027 http = Application.get_env(:pleroma, :http)
2030 Application.delete_env(:pleroma, :key1)
2031 Application.delete_env(:pleroma, :key2)
2032 Application.delete_env(:pleroma, :key3)
2033 Application.delete_env(:pleroma, :key4)
2034 Application.delete_env(:pleroma, :keyaa1)
2035 Application.delete_env(:pleroma, :keyaa2)
2036 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
2037 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
2038 Application.put_env(:pleroma, :http, http)
2039 Application.put_env(:tesla, :adapter, Tesla.Mock)
2040 Restarter.Pleroma.refresh()
2044 clear_config(:configurable_from_database) do
2045 Config.put(:configurable_from_database, true)
2048 @tag capture_log: true
2049 test "create new config setting in db", %{conn: conn} do
2050 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2051 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
2054 post(conn, "/api/pleroma/admin/config", %{
2056 %{group: ":pleroma", key: ":key1", value: "value1"},
2058 group: ":ueberauth",
2060 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
2066 ":nested_1" => "nested_value1",
2068 %{":nested_22" => "nested_value222"},
2069 %{":nested_33" => %{":nested_44" => "nested_444"}}
2077 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
2078 %{"nested_4" => true}
2084 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
2089 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
2094 assert json_response(conn, 200) == %{
2097 "group" => ":pleroma",
2099 "value" => "value1",
2103 "group" => ":ueberauth",
2104 "key" => "Ueberauth",
2105 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
2106 "db" => [":consumer_secret"]
2109 "group" => ":pleroma",
2112 ":nested_1" => "nested_value1",
2114 %{":nested_22" => "nested_value222"},
2115 %{":nested_33" => %{":nested_44" => "nested_444"}}
2121 "group" => ":pleroma",
2124 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
2125 %{"nested_4" => true}
2130 "group" => ":pleroma",
2132 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
2138 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
2144 assert Application.get_env(:pleroma, :key1) == "value1"
2146 assert Application.get_env(:pleroma, :key2) == %{
2147 nested_1: "nested_value1",
2149 %{nested_22: "nested_value222"},
2150 %{nested_33: %{nested_44: "nested_444"}}
2154 assert Application.get_env(:pleroma, :key3) == [
2155 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
2156 %{"nested_4" => true}
2159 assert Application.get_env(:pleroma, :key4) == %{
2160 "endpoint" => "https://example.com",
2164 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
2167 test "save configs setting without explicit key", %{conn: conn} do
2168 level = Application.get_env(:quack, :level)
2169 meta = Application.get_env(:quack, :meta)
2170 webhook_url = Application.get_env(:quack, :webhook_url)
2173 Application.put_env(:quack, :level, level)
2174 Application.put_env(:quack, :meta, meta)
2175 Application.put_env(:quack, :webhook_url, webhook_url)
2179 post(conn, "/api/pleroma/admin/config", %{
2193 key: ":webhook_url",
2194 value: "https://hooks.slack.com/services/KEY"
2199 assert json_response(conn, 200) == %{
2202 "group" => ":quack",
2208 "group" => ":quack",
2210 "value" => [":none"],
2214 "group" => ":quack",
2215 "key" => ":webhook_url",
2216 "value" => "https://hooks.slack.com/services/KEY",
2217 "db" => [":webhook_url"]
2222 assert Application.get_env(:quack, :level) == :info
2223 assert Application.get_env(:quack, :meta) == [:none]
2224 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
2227 test "saving config with partial update", %{conn: conn} do
2228 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
2231 post(conn, "/api/pleroma/admin/config", %{
2233 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
2237 assert json_response(conn, 200) == %{
2240 "group" => ":pleroma",
2243 %{"tuple" => [":key1", 1]},
2244 %{"tuple" => [":key2", 2]},
2245 %{"tuple" => [":key3", 3]}
2247 "db" => [":key1", ":key2", ":key3"]
2253 test "saving config which need pleroma reboot", %{conn: conn} do
2254 chat = Config.get(:chat)
2255 on_exit(fn -> Config.put(:chat, chat) end)
2259 "/api/pleroma/admin/config",
2262 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2266 |> json_response(200) == %{
2269 "db" => [":enabled"],
2270 "group" => ":pleroma",
2272 "value" => [%{"tuple" => [":enabled", true]}]
2275 "need_reboot" => true
2280 |> get("/api/pleroma/admin/config")
2281 |> json_response(200)
2283 assert configs["need_reboot"]
2286 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2287 end) =~ "pleroma restarted"
2291 |> get("/api/pleroma/admin/config")
2292 |> json_response(200)
2294 assert configs["need_reboot"] == false
2297 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
2298 chat = Config.get(:chat)
2299 on_exit(fn -> Config.put(:chat, chat) end)
2303 "/api/pleroma/admin/config",
2306 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2310 |> json_response(200) == %{
2313 "db" => [":enabled"],
2314 "group" => ":pleroma",
2316 "value" => [%{"tuple" => [":enabled", true]}]
2319 "need_reboot" => true
2322 assert post(conn, "/api/pleroma/admin/config", %{
2324 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
2327 |> json_response(200) == %{
2330 "group" => ":pleroma",
2333 %{"tuple" => [":key3", 3]}
2338 "need_reboot" => true
2342 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2343 end) =~ "pleroma restarted"
2347 |> get("/api/pleroma/admin/config")
2348 |> json_response(200)
2350 assert configs["need_reboot"] == false
2353 test "saving config with nested merge", %{conn: conn} do
2355 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
2358 post(conn, "/api/pleroma/admin/config", %{
2361 group: config.group,
2364 %{"tuple" => [":key3", 3]},
2369 %{"tuple" => [":k2", 1]},
2370 %{"tuple" => [":k3", 3]}
2379 assert json_response(conn, 200) == %{
2382 "group" => ":pleroma",
2385 %{"tuple" => [":key1", 1]},
2386 %{"tuple" => [":key3", 3]},
2391 %{"tuple" => [":k1", 1]},
2392 %{"tuple" => [":k2", 1]},
2393 %{"tuple" => [":k3", 3]}
2398 "db" => [":key1", ":key3", ":key2"]
2404 test "saving special atoms", %{conn: conn} do
2406 post(conn, "/api/pleroma/admin/config", %{
2409 "group" => ":pleroma",
2415 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2423 assert json_response(conn, 200) == %{
2426 "group" => ":pleroma",
2432 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2436 "db" => [":ssl_options"]
2441 assert Application.get_env(:pleroma, :key1) == [
2442 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
2446 test "saving full setting if value is in full_key_update list", %{conn: conn} do
2447 backends = Application.get_env(:logger, :backends)
2448 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
2454 value: :erlang.term_to_binary([])
2458 post(conn, "/api/pleroma/admin/config", %{
2461 group: config.group,
2463 value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}]
2468 assert json_response(conn, 200) == %{
2471 "group" => ":logger",
2472 "key" => ":backends",
2475 %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}
2477 "db" => [":backends"]
2482 assert Application.get_env(:logger, :backends) == [
2484 {ExSyslogger, :ex_syslogger}
2489 Logger.warn("Ooops...")
2493 test "saving full setting if value is not keyword", %{conn: conn} do
2498 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
2502 post(conn, "/api/pleroma/admin/config", %{
2504 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
2508 assert json_response(conn, 200) == %{
2511 "group" => ":tesla",
2512 "key" => ":adapter",
2513 "value" => "Tesla.Adapter.Httpc",
2514 "db" => [":adapter"]
2520 test "update config setting & delete with fallback to default value", %{
2525 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2526 config1 = insert(:config, key: ":keyaa1")
2527 config2 = insert(:config, key: ":keyaa2")
2531 group: ":ueberauth",
2536 post(conn, "/api/pleroma/admin/config", %{
2538 %{group: config1.group, key: config1.key, value: "another_value"},
2539 %{group: config2.group, key: config2.key, value: "another_value"}
2543 assert json_response(conn, 200) == %{
2546 "group" => ":pleroma",
2547 "key" => config1.key,
2548 "value" => "another_value",
2552 "group" => ":pleroma",
2553 "key" => config2.key,
2554 "value" => "another_value",
2560 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2561 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
2562 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
2566 |> assign(:user, admin)
2567 |> assign(:token, token)
2568 |> post("/api/pleroma/admin/config", %{
2570 %{group: config2.group, key: config2.key, delete: true},
2572 group: ":ueberauth",
2579 assert json_response(conn, 200) == %{
2583 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
2584 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
2587 test "common config example", %{conn: conn} do
2588 adapter = Application.get_env(:tesla, :adapter)
2589 on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
2592 post(conn, "/api/pleroma/admin/config", %{
2595 "group" => ":pleroma",
2596 "key" => "Pleroma.Captcha.NotReal",
2598 %{"tuple" => [":enabled", false]},
2599 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2600 %{"tuple" => [":seconds_valid", 60]},
2601 %{"tuple" => [":path", ""]},
2602 %{"tuple" => [":key1", nil]},
2603 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2604 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2605 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2606 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2607 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2608 %{"tuple" => [":name", "Pleroma"]}
2612 "group" => ":tesla",
2613 "key" => ":adapter",
2614 "value" => "Tesla.Adapter.Httpc"
2619 assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
2620 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2622 assert json_response(conn, 200) == %{
2625 "group" => ":pleroma",
2626 "key" => "Pleroma.Captcha.NotReal",
2628 %{"tuple" => [":enabled", false]},
2629 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2630 %{"tuple" => [":seconds_valid", 60]},
2631 %{"tuple" => [":path", ""]},
2632 %{"tuple" => [":key1", nil]},
2633 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2634 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2635 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2636 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2637 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2638 %{"tuple" => [":name", "Pleroma"]}
2655 "group" => ":tesla",
2656 "key" => ":adapter",
2657 "value" => "Tesla.Adapter.Httpc",
2658 "db" => [":adapter"]
2664 test "tuples with more than two values", %{conn: conn} do
2666 post(conn, "/api/pleroma/admin/config", %{
2669 "group" => ":pleroma",
2670 "key" => "Pleroma.Web.Endpoint.NotReal",
2686 "/api/v1/streaming",
2687 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2694 "Phoenix.Endpoint.CowboyWebSocket",
2697 "Phoenix.Transports.WebSocket",
2700 "Pleroma.Web.Endpoint",
2701 "Pleroma.Web.UserSocket",
2712 "Phoenix.Endpoint.Cowboy2Handler",
2713 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2730 assert json_response(conn, 200) == %{
2733 "group" => ":pleroma",
2734 "key" => "Pleroma.Web.Endpoint.NotReal",
2750 "/api/v1/streaming",
2751 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2758 "Phoenix.Endpoint.CowboyWebSocket",
2761 "Phoenix.Transports.WebSocket",
2764 "Pleroma.Web.Endpoint",
2765 "Pleroma.Web.UserSocket",
2776 "Phoenix.Endpoint.Cowboy2Handler",
2777 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2796 test "settings with nesting map", %{conn: conn} do
2798 post(conn, "/api/pleroma/admin/config", %{
2801 "group" => ":pleroma",
2804 %{"tuple" => [":key2", "some_val"]},
2809 ":max_options" => 20,
2810 ":max_option_chars" => 200,
2811 ":min_expiration" => 0,
2812 ":max_expiration" => 31_536_000,
2814 ":max_options" => 20,
2815 ":max_option_chars" => 200,
2816 ":min_expiration" => 0,
2817 ":max_expiration" => 31_536_000
2827 assert json_response(conn, 200) ==
2831 "group" => ":pleroma",
2834 %{"tuple" => [":key2", "some_val"]},
2839 ":max_expiration" => 31_536_000,
2840 ":max_option_chars" => 200,
2841 ":max_options" => 20,
2842 ":min_expiration" => 0,
2844 ":max_expiration" => 31_536_000,
2845 ":max_option_chars" => 200,
2846 ":max_options" => 20,
2847 ":min_expiration" => 0
2853 "db" => [":key2", ":key3"]
2859 test "value as map", %{conn: conn} do
2861 post(conn, "/api/pleroma/admin/config", %{
2864 "group" => ":pleroma",
2866 "value" => %{"key" => "some_val"}
2871 assert json_response(conn, 200) ==
2875 "group" => ":pleroma",
2877 "value" => %{"key" => "some_val"},
2884 test "queues key as atom", %{conn: conn} do
2886 post(conn, "/api/pleroma/admin/config", %{
2892 %{"tuple" => [":federator_incoming", 50]},
2893 %{"tuple" => [":federator_outgoing", 50]},
2894 %{"tuple" => [":web_push", 50]},
2895 %{"tuple" => [":mailer", 10]},
2896 %{"tuple" => [":transmogrifier", 20]},
2897 %{"tuple" => [":scheduled_activities", 10]},
2898 %{"tuple" => [":background", 5]}
2904 assert json_response(conn, 200) == %{
2910 %{"tuple" => [":federator_incoming", 50]},
2911 %{"tuple" => [":federator_outgoing", 50]},
2912 %{"tuple" => [":web_push", 50]},
2913 %{"tuple" => [":mailer", 10]},
2914 %{"tuple" => [":transmogrifier", 20]},
2915 %{"tuple" => [":scheduled_activities", 10]},
2916 %{"tuple" => [":background", 5]}
2919 ":federator_incoming",
2920 ":federator_outgoing",
2924 ":scheduled_activities",
2932 test "delete part of settings by atom subkeys", %{conn: conn} do
2936 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2940 post(conn, "/api/pleroma/admin/config", %{
2943 group: config.group,
2945 subkeys: [":subkey1", ":subkey3"],
2951 assert json_response(conn, 200) == %{
2954 "group" => ":pleroma",
2956 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2957 "db" => [":subkey2"]
2963 test "proxy tuple localhost", %{conn: conn} do
2965 post(conn, "/api/pleroma/admin/config", %{
2971 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2972 %{"tuple" => [":send_user_agent", false]}
2978 assert json_response(conn, 200) == %{
2981 "group" => ":pleroma",
2984 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
2985 %{"tuple" => [":send_user_agent", false]}
2987 "db" => [":proxy_url", ":send_user_agent"]
2993 test "proxy tuple domain", %{conn: conn} do
2995 post(conn, "/api/pleroma/admin/config", %{
3001 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
3002 %{"tuple" => [":send_user_agent", false]}
3008 assert json_response(conn, 200) == %{
3011 "group" => ":pleroma",
3014 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
3015 %{"tuple" => [":send_user_agent", false]}
3017 "db" => [":proxy_url", ":send_user_agent"]
3023 test "proxy tuple ip", %{conn: conn} do
3025 post(conn, "/api/pleroma/admin/config", %{
3031 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
3032 %{"tuple" => [":send_user_agent", false]}
3038 assert json_response(conn, 200) == %{
3041 "group" => ":pleroma",
3044 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
3045 %{"tuple" => [":send_user_agent", false]}
3047 "db" => [":proxy_url", ":send_user_agent"]
3054 describe "GET /api/pleroma/admin/restart" do
3055 clear_config(:configurable_from_database) do
3056 Config.put(:configurable_from_database, true)
3059 test "pleroma restarts", %{conn: conn} do
3061 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
3062 end) =~ "pleroma restarted"
3064 refute Restarter.Pleroma.need_reboot?()
3068 test "need_reboot flag", %{conn: conn} do
3070 |> get("/api/pleroma/admin/need_reboot")
3071 |> json_response(200) == %{"need_reboot" => false}
3073 Restarter.Pleroma.need_reboot()
3076 |> get("/api/pleroma/admin/need_reboot")
3077 |> json_response(200) == %{"need_reboot" => true}
3079 on_exit(fn -> Restarter.Pleroma.refresh() end)
3082 describe "GET /api/pleroma/admin/statuses" do
3083 test "returns all public, unlisted, and direct statuses", %{conn: conn, admin: admin} do
3084 blocked = insert(:user)
3085 user = insert(:user)
3086 User.block(admin, blocked)
3089 CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
3091 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
3092 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
3093 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
3094 {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
3098 |> get("/api/pleroma/admin/statuses")
3099 |> json_response(200)
3101 refute "private" in Enum.map(response, & &1["visibility"])
3102 assert length(response) == 4
3105 test "returns only local statuses with local_only on", %{conn: conn} do
3106 user = insert(:user)
3107 remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
3108 insert(:note_activity, user: user, local: true)
3109 insert(:note_activity, user: remote_user, local: false)
3113 |> get("/api/pleroma/admin/statuses?local_only=true")
3114 |> json_response(200)
3116 assert length(response) == 1
3119 test "returns private statuses with godmode on", %{conn: conn} do
3120 user = insert(:user)
3121 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
3122 {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
3123 conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
3124 assert json_response(conn, 200) |> length() == 2
3128 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
3130 user = insert(:user)
3132 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
3133 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
3134 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
3136 insert(:note_activity, user: user, published: date1)
3137 insert(:note_activity, user: user, published: date2)
3138 insert(:note_activity, user: user, published: date3)
3143 test "renders user's statuses", %{conn: conn, user: user} do
3144 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
3146 assert json_response(conn, 200) |> length() == 3
3149 test "renders user's statuses with a limit", %{conn: conn, user: user} do
3150 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
3152 assert json_response(conn, 200) |> length() == 2
3155 test "doesn't return private statuses by default", %{conn: conn, user: user} do
3156 {:ok, _private_status} =
3157 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
3159 {:ok, _public_status} =
3160 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
3162 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
3164 assert json_response(conn, 200) |> length() == 4
3167 test "returns private statuses with godmode on", %{conn: conn, user: user} do
3168 {:ok, _private_status} =
3169 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
3171 {:ok, _public_status} =
3172 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
3174 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
3176 assert json_response(conn, 200) |> length() == 5
3179 test "excludes reblogs by default", %{conn: conn, user: user} do
3180 other_user = insert(:user)
3181 {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
3182 {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
3184 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
3185 assert json_response(conn_res, 200) |> length() == 0
3188 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
3190 assert json_response(conn_res, 200) |> length() == 1
3194 describe "GET /api/pleroma/admin/moderation_log" do
3196 moderator = insert(:user, is_moderator: true)
3198 %{moderator: moderator}
3201 test "returns the log", %{conn: conn, admin: admin} do
3202 Repo.insert(%ModerationLog{
3206 "nickname" => admin.nickname,
3209 action: "relay_follow",
3210 target: "https://example.org/relay"
3212 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3215 Repo.insert(%ModerationLog{
3219 "nickname" => admin.nickname,
3222 action: "relay_unfollow",
3223 target: "https://example.org/relay"
3225 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3228 conn = get(conn, "/api/pleroma/admin/moderation_log")
3230 response = json_response(conn, 200)
3231 [first_entry, second_entry] = response["items"]
3233 assert response["total"] == 2
3234 assert first_entry["data"]["action"] == "relay_unfollow"
3236 assert first_entry["message"] ==
3237 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3239 assert second_entry["data"]["action"] == "relay_follow"
3241 assert second_entry["message"] ==
3242 "@#{admin.nickname} followed relay: https://example.org/relay"
3245 test "returns the log with pagination", %{conn: conn, admin: admin} do
3246 Repo.insert(%ModerationLog{
3250 "nickname" => admin.nickname,
3253 action: "relay_follow",
3254 target: "https://example.org/relay"
3256 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3259 Repo.insert(%ModerationLog{
3263 "nickname" => admin.nickname,
3266 action: "relay_unfollow",
3267 target: "https://example.org/relay"
3269 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3272 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3274 response1 = json_response(conn1, 200)
3275 [first_entry] = response1["items"]
3277 assert response1["total"] == 2
3278 assert response1["items"] |> length() == 1
3279 assert first_entry["data"]["action"] == "relay_unfollow"
3281 assert first_entry["message"] ==
3282 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3284 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3286 response2 = json_response(conn2, 200)
3287 [second_entry] = response2["items"]
3289 assert response2["total"] == 2
3290 assert response2["items"] |> length() == 1
3291 assert second_entry["data"]["action"] == "relay_follow"
3293 assert second_entry["message"] ==
3294 "@#{admin.nickname} followed relay: https://example.org/relay"
3297 test "filters log by date", %{conn: conn, admin: admin} do
3298 first_date = "2017-08-15T15:47:06Z"
3299 second_date = "2017-08-20T15:47:06Z"
3301 Repo.insert(%ModerationLog{
3305 "nickname" => admin.nickname,
3308 action: "relay_follow",
3309 target: "https://example.org/relay"
3311 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3314 Repo.insert(%ModerationLog{
3318 "nickname" => admin.nickname,
3321 action: "relay_unfollow",
3322 target: "https://example.org/relay"
3324 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3330 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3333 response1 = json_response(conn1, 200)
3334 [first_entry] = response1["items"]
3336 assert response1["total"] == 1
3337 assert first_entry["data"]["action"] == "relay_unfollow"
3339 assert first_entry["message"] ==
3340 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3343 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3344 Repo.insert(%ModerationLog{
3348 "nickname" => admin.nickname,
3351 action: "relay_follow",
3352 target: "https://example.org/relay"
3356 Repo.insert(%ModerationLog{
3359 "id" => moderator.id,
3360 "nickname" => moderator.nickname,
3363 action: "relay_unfollow",
3364 target: "https://example.org/relay"
3368 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3370 response1 = json_response(conn1, 200)
3371 [first_entry] = response1["items"]
3373 assert response1["total"] == 1
3374 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3377 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3378 ModerationLog.insert_log(%{
3380 action: "relay_follow",
3381 target: "https://example.org/relay"
3384 ModerationLog.insert_log(%{
3386 action: "relay_unfollow",
3387 target: "https://example.org/relay"
3390 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3392 response1 = json_response(conn1, 200)
3393 [first_entry] = response1["items"]
3395 assert response1["total"] == 1
3397 assert get_in(first_entry, ["data", "message"]) ==
3398 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3402 describe "GET /users/:nickname/credentials" do
3403 test "gets the user credentials", %{conn: conn} do
3404 user = insert(:user)
3405 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3407 response = assert json_response(conn, 200)
3408 assert response["email"] == user.email
3411 test "returns 403 if requested by a non-admin" do
3412 user = insert(:user)
3416 |> assign(:user, user)
3417 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3419 assert json_response(conn, :forbidden)
3423 describe "PATCH /users/:nickname/credentials" do
3424 test "changes password and email", %{conn: conn, admin: admin} do
3425 user = insert(:user)
3426 assert user.password_reset_pending == false
3429 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3430 "password" => "new_password",
3431 "email" => "new_email@example.com",
3432 "name" => "new_name"
3435 assert json_response(conn, 200) == %{"status" => "success"}
3437 ObanHelpers.perform_all()
3439 updated_user = User.get_by_id(user.id)
3441 assert updated_user.email == "new_email@example.com"
3442 assert updated_user.name == "new_name"
3443 assert updated_user.password_hash != user.password_hash
3444 assert updated_user.password_reset_pending == true
3446 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3448 assert ModerationLog.get_log_entry_message(log_entry1) ==
3449 "@#{admin.nickname} updated users: @#{user.nickname}"
3451 assert ModerationLog.get_log_entry_message(log_entry2) ==
3452 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3455 test "returns 403 if requested by a non-admin" do
3456 user = insert(:user)
3460 |> assign(:user, user)
3461 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3462 "password" => "new_password",
3463 "email" => "new_email@example.com",
3464 "name" => "new_name"
3467 assert json_response(conn, :forbidden)
3471 describe "PATCH /users/:nickname/force_password_reset" do
3472 test "sets password_reset_pending to true", %{conn: conn} do
3473 user = insert(:user)
3474 assert user.password_reset_pending == false
3477 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3479 assert json_response(conn, 204) == ""
3481 ObanHelpers.perform_all()
3483 assert User.get_by_id(user.id).password_reset_pending == true
3487 describe "relays" do
3488 test "POST /relay", %{conn: conn, admin: admin} do
3490 post(conn, "/api/pleroma/admin/relay", %{
3491 relay_url: "http://mastodon.example.org/users/admin"
3494 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3496 log_entry = Repo.one(ModerationLog)
3498 assert ModerationLog.get_log_entry_message(log_entry) ==
3499 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3502 test "GET /relay", %{conn: conn} do
3503 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3505 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3506 |> Enum.each(fn ap_id ->
3507 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3508 User.follow(relay_user, user)
3511 conn = get(conn, "/api/pleroma/admin/relay")
3513 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3516 test "DELETE /relay", %{conn: conn, admin: admin} do
3517 post(conn, "/api/pleroma/admin/relay", %{
3518 relay_url: "http://mastodon.example.org/users/admin"
3522 delete(conn, "/api/pleroma/admin/relay", %{
3523 relay_url: "http://mastodon.example.org/users/admin"
3526 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3528 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3530 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3531 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3533 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3534 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3538 describe "instances" do
3539 test "GET /instances/:instance/statuses", %{conn: conn} do
3540 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3541 user2 = insert(:user, local: false, nickname: "test@test.com")
3542 insert_pair(:note_activity, user: user)
3543 activity = insert(:note_activity, user: user2)
3545 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3547 response = json_response(ret_conn, 200)
3549 assert length(response) == 2
3551 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3553 response = json_response(ret_conn, 200)
3555 assert length(response) == 1
3557 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3559 response = json_response(ret_conn, 200)
3561 assert Enum.empty?(response)
3563 CommonAPI.repeat(activity.id, user)
3565 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3566 response = json_response(ret_conn, 200)
3567 assert length(response) == 2
3569 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3570 response = json_response(ret_conn, 200)
3571 assert length(response) == 3
3575 describe "PATCH /confirm_email" do
3576 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3577 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3579 assert first_user.confirmation_pending == true
3580 assert second_user.confirmation_pending == true
3583 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3585 first_user.nickname,
3586 second_user.nickname
3590 assert ret_conn.status == 200
3592 assert first_user.confirmation_pending == true
3593 assert second_user.confirmation_pending == true
3595 log_entry = Repo.one(ModerationLog)
3597 assert ModerationLog.get_log_entry_message(log_entry) ==
3598 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3599 second_user.nickname
3604 describe "PATCH /resend_confirmation_email" do
3605 test "it resend emails for two users", %{conn: conn, admin: admin} do
3606 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3609 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3611 first_user.nickname,
3612 second_user.nickname
3616 assert ret_conn.status == 200
3618 log_entry = Repo.one(ModerationLog)
3620 assert ModerationLog.get_log_entry_message(log_entry) ==
3621 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3622 second_user.nickname
3627 describe "POST /reports/:id/notes" do
3628 setup %{conn: conn, admin: admin} do
3629 [reporter, target_user] = insert_pair(:user)
3630 activity = insert(:note_activity, user: target_user)
3632 {:ok, %{id: report_id}} =
3633 CommonAPI.report(reporter, %{
3634 "account_id" => target_user.id,
3635 "comment" => "I feel offended",
3636 "status_ids" => [activity.id]
3639 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3640 content: "this is disgusting!"
3643 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3644 content: "this is disgusting2!"
3649 report_id: report_id
3653 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3654 [note, _] = Repo.all(ReportNote)
3657 activity_id: ^report_id,
3658 content: "this is disgusting!",
3663 test "it returns reports with notes", %{conn: conn, admin: admin} do
3664 conn = get(conn, "/api/pleroma/admin/reports")
3666 response = json_response(conn, 200)
3667 notes = hd(response["reports"])["notes"]
3670 assert note["user"]["nickname"] == admin.nickname
3671 assert note["content"] == "this is disgusting!"
3672 assert note["created_at"]
3673 assert response["total"] == 1
3676 test "it deletes the note", %{conn: conn, report_id: report_id} do
3677 assert ReportNote |> Repo.all() |> length() == 2
3679 [note, _] = Repo.all(ReportNote)
3681 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3683 assert ReportNote |> Repo.all() |> length() == 1
3687 test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do
3688 admin = insert(:user, is_admin: true)
3691 assign(conn, :user, admin)
3692 |> get("/api/pleroma/admin/config/descriptions")
3694 assert [child | _others] = json_response(conn, 200)
3696 assert child["children"]
3698 assert String.starts_with?(child["group"], ":")
3699 assert child["description"]
3702 describe "/api/pleroma/admin/stats" do
3703 test "status visibility count", %{conn: conn} do
3704 admin = insert(:user, is_admin: true)
3705 user = insert(:user)
3706 CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
3707 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3708 CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
3712 |> assign(:user, admin)
3713 |> get("/api/pleroma/admin/stats")
3714 |> json_response(200)
3716 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3717 response["status_visibility"]
3722 # Needed for testing
3723 defmodule Pleroma.Web.Endpoint.NotReal do
3726 defmodule Pleroma.Captcha.NotReal do