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
9 import ExUnit.CaptureLog
11 import Pleroma.Factory
13 alias Pleroma.Activity
17 alias Pleroma.ModerationLog
19 alias Pleroma.ReportNote
20 alias Pleroma.Tests.ObanHelpers
22 alias Pleroma.UserInviteToken
24 alias Pleroma.Web.ActivityPub.Relay
25 alias Pleroma.Web.CommonAPI
26 alias Pleroma.Web.MediaProxy
29 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
35 admin = insert(:user, is_admin: true)
36 token = insert(:oauth_admin_token, user: admin)
40 |> assign(:user, admin)
41 |> assign(:token, token)
43 {:ok, %{admin: admin, token: token, conn: conn}}
46 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
47 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
49 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
52 url = "/api/pleroma/admin/users/#{user.nickname}"
54 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
55 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
56 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
58 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
59 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
62 for good_token <- [good_token1, good_token2, good_token3] do
65 |> assign(:user, admin)
66 |> assign(:token, good_token)
69 assert json_response(conn, 200)
72 for good_token <- [good_token1, good_token2, good_token3] do
76 |> assign(:token, good_token)
79 assert json_response(conn, :forbidden)
82 for bad_token <- [bad_token1, bad_token2, bad_token3] do
85 |> assign(:user, admin)
86 |> assign(:token, bad_token)
89 assert json_response(conn, :forbidden)
94 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
95 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
97 test "GET /api/pleroma/admin/users/:nickname requires " <>
98 "read:accounts or admin:read:accounts or broader scope",
101 url = "/api/pleroma/admin/users/#{user.nickname}"
103 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
104 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
105 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
106 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
107 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
109 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
111 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
112 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
115 for good_token <- good_tokens do
118 |> assign(:user, admin)
119 |> assign(:token, good_token)
122 assert json_response(conn, 200)
125 for good_token <- good_tokens do
128 |> assign(:user, nil)
129 |> assign(:token, good_token)
132 assert json_response(conn, :forbidden)
135 for bad_token <- [bad_token1, bad_token2, bad_token3] do
138 |> assign(:user, admin)
139 |> assign(:token, bad_token)
142 assert json_response(conn, :forbidden)
147 describe "DELETE /api/pleroma/admin/users" do
148 test "single user", %{admin: admin, conn: conn} do
150 clear_config([:instance, :federating], true)
152 with_mock Pleroma.Web.Federator,
153 publish: fn _ -> nil end do
156 |> put_req_header("accept", "application/json")
157 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
159 ObanHelpers.perform_all()
161 assert User.get_by_nickname(user.nickname).deactivated
163 log_entry = Repo.one(ModerationLog)
165 assert ModerationLog.get_log_entry_message(log_entry) ==
166 "@#{admin.nickname} deleted users: @#{user.nickname}"
168 assert json_response(conn, 200) == [user.nickname]
170 assert called(Pleroma.Web.Federator.publish(:_))
174 test "multiple users", %{admin: admin, conn: conn} do
175 user_one = insert(:user)
176 user_two = insert(:user)
180 |> put_req_header("accept", "application/json")
181 |> delete("/api/pleroma/admin/users", %{
182 nicknames: [user_one.nickname, user_two.nickname]
185 log_entry = Repo.one(ModerationLog)
187 assert ModerationLog.get_log_entry_message(log_entry) ==
188 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
190 response = json_response(conn, 200)
191 assert response -- [user_one.nickname, user_two.nickname] == []
195 describe "/api/pleroma/admin/users" do
196 test "Create", %{conn: conn} do
199 |> put_req_header("accept", "application/json")
200 |> post("/api/pleroma/admin/users", %{
203 "nickname" => "lain",
204 "email" => "lain@example.org",
208 "nickname" => "lain2",
209 "email" => "lain2@example.org",
215 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
216 assert response == ["success", "success"]
218 log_entry = Repo.one(ModerationLog)
220 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
223 test "Cannot create user with existing email", %{conn: conn} do
228 |> put_req_header("accept", "application/json")
229 |> post("/api/pleroma/admin/users", %{
232 "nickname" => "lain",
233 "email" => user.email,
239 assert json_response(conn, 409) == [
243 "email" => user.email,
246 "error" => "email has already been taken",
252 test "Cannot create user with existing nickname", %{conn: conn} do
257 |> put_req_header("accept", "application/json")
258 |> post("/api/pleroma/admin/users", %{
261 "nickname" => user.nickname,
262 "email" => "someuser@plerama.social",
268 assert json_response(conn, 409) == [
272 "email" => "someuser@plerama.social",
273 "nickname" => user.nickname
275 "error" => "nickname has already been taken",
281 test "Multiple user creation works in transaction", %{conn: conn} do
286 |> put_req_header("accept", "application/json")
287 |> post("/api/pleroma/admin/users", %{
290 "nickname" => "newuser",
291 "email" => "newuser@pleroma.social",
295 "nickname" => "lain",
296 "email" => user.email,
302 assert json_response(conn, 409) == [
306 "email" => user.email,
309 "error" => "email has already been taken",
315 "email" => "newuser@pleroma.social",
316 "nickname" => "newuser"
323 assert User.get_by_nickname("newuser") === nil
327 describe "/api/pleroma/admin/users/:nickname" do
328 test "Show", %{conn: conn} do
331 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
334 "deactivated" => false,
335 "id" => to_string(user.id),
337 "nickname" => user.nickname,
338 "roles" => %{"admin" => false, "moderator" => false},
340 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
341 "display_name" => HTML.strip_tags(user.name || user.nickname),
342 "confirmation_pending" => false
345 assert expected == json_response(conn, 200)
348 test "when the user doesn't exist", %{conn: conn} do
351 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
353 assert %{"error" => "Not found"} == json_response(conn, 404)
357 describe "/api/pleroma/admin/users/follow" do
358 test "allows to force-follow another user", %{admin: admin, conn: conn} do
360 follower = insert(:user)
363 |> put_req_header("accept", "application/json")
364 |> post("/api/pleroma/admin/users/follow", %{
365 "follower" => follower.nickname,
366 "followed" => user.nickname
369 user = User.get_cached_by_id(user.id)
370 follower = User.get_cached_by_id(follower.id)
372 assert User.following?(follower, user)
374 log_entry = Repo.one(ModerationLog)
376 assert ModerationLog.get_log_entry_message(log_entry) ==
377 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
381 describe "/api/pleroma/admin/users/unfollow" do
382 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
384 follower = insert(:user)
386 User.follow(follower, user)
389 |> put_req_header("accept", "application/json")
390 |> post("/api/pleroma/admin/users/unfollow", %{
391 "follower" => follower.nickname,
392 "followed" => user.nickname
395 user = User.get_cached_by_id(user.id)
396 follower = User.get_cached_by_id(follower.id)
398 refute User.following?(follower, user)
400 log_entry = Repo.one(ModerationLog)
402 assert ModerationLog.get_log_entry_message(log_entry) ==
403 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
407 describe "PUT /api/pleroma/admin/users/tag" do
408 setup %{conn: conn} do
409 user1 = insert(:user, %{tags: ["x"]})
410 user2 = insert(:user, %{tags: ["y"]})
411 user3 = insert(:user, %{tags: ["unchanged"]})
415 |> put_req_header("accept", "application/json")
417 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
418 "#{user2.nickname}&tags[]=foo&tags[]=bar"
421 %{conn: conn, user1: user1, user2: user2, user3: user3}
424 test "it appends specified tags to users with specified nicknames", %{
430 assert json_response(conn, :no_content)
431 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
432 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
434 log_entry = Repo.one(ModerationLog)
437 [user1.nickname, user2.nickname]
438 |> Enum.map(&"@#{&1}")
441 tags = ["foo", "bar"] |> Enum.join(", ")
443 assert ModerationLog.get_log_entry_message(log_entry) ==
444 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
447 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
448 assert json_response(conn, :no_content)
449 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
453 describe "DELETE /api/pleroma/admin/users/tag" do
454 setup %{conn: conn} do
455 user1 = insert(:user, %{tags: ["x"]})
456 user2 = insert(:user, %{tags: ["y", "z"]})
457 user3 = insert(:user, %{tags: ["unchanged"]})
461 |> put_req_header("accept", "application/json")
463 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
464 "#{user2.nickname}&tags[]=x&tags[]=z"
467 %{conn: conn, user1: user1, user2: user2, user3: user3}
470 test "it removes specified tags from users with specified nicknames", %{
476 assert json_response(conn, :no_content)
477 assert User.get_cached_by_id(user1.id).tags == []
478 assert User.get_cached_by_id(user2.id).tags == ["y"]
480 log_entry = Repo.one(ModerationLog)
483 [user1.nickname, user2.nickname]
484 |> Enum.map(&"@#{&1}")
487 tags = ["x", "z"] |> Enum.join(", ")
489 assert ModerationLog.get_log_entry_message(log_entry) ==
490 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
493 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
494 assert json_response(conn, :no_content)
495 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
499 describe "/api/pleroma/admin/users/:nickname/permission_group" do
500 test "GET is giving user_info", %{admin: admin, conn: conn} do
503 |> put_req_header("accept", "application/json")
504 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
506 assert json_response(conn, 200) == %{
508 "is_moderator" => false
512 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
517 |> put_req_header("accept", "application/json")
518 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
520 assert json_response(conn, 200) == %{
524 log_entry = Repo.one(ModerationLog)
526 assert ModerationLog.get_log_entry_message(log_entry) ==
527 "@#{admin.nickname} made @#{user.nickname} admin"
530 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
531 user_one = insert(:user)
532 user_two = insert(:user)
536 |> put_req_header("accept", "application/json")
537 |> post("/api/pleroma/admin/users/permission_group/admin", %{
538 nicknames: [user_one.nickname, user_two.nickname]
541 assert json_response(conn, 200) == %{"is_admin" => true}
543 log_entry = Repo.one(ModerationLog)
545 assert ModerationLog.get_log_entry_message(log_entry) ==
546 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
549 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
550 user = insert(:user, is_admin: true)
554 |> put_req_header("accept", "application/json")
555 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
557 assert json_response(conn, 200) == %{"is_admin" => false}
559 log_entry = Repo.one(ModerationLog)
561 assert ModerationLog.get_log_entry_message(log_entry) ==
562 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
565 test "/:right DELETE, can remove from a permission group (multiple)", %{
569 user_one = insert(:user, is_admin: true)
570 user_two = insert(:user, is_admin: true)
574 |> put_req_header("accept", "application/json")
575 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
576 nicknames: [user_one.nickname, user_two.nickname]
579 assert json_response(conn, 200) == %{"is_admin" => false}
581 log_entry = Repo.one(ModerationLog)
583 assert ModerationLog.get_log_entry_message(log_entry) ==
584 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
590 describe "POST /api/pleroma/admin/email_invite, with valid config" do
591 setup do: clear_config([:instance, :registrations_open], false)
592 setup do: clear_config([:instance, :invites_enabled], true)
594 test "sends invitation and returns 204", %{admin: admin, conn: conn} do
595 recipient_email = "foo@bar.com"
596 recipient_name = "J. D."
601 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
604 assert json_response(conn, :no_content)
606 token_record = List.last(Repo.all(Pleroma.UserInviteToken))
608 refute token_record.used
610 notify_email = Config.get([:instance, :notify_email])
611 instance_name = Config.get([:instance, :name])
614 Pleroma.Emails.UserEmail.user_invitation_email(
621 Swoosh.TestAssertions.assert_email_sent(
622 from: {instance_name, notify_email},
623 to: {recipient_name, recipient_email},
624 html_body: email.html_body
628 test "it returns 403 if requested by a non-admin" do
629 non_admin_user = insert(:user)
630 token = insert(:oauth_token, user: non_admin_user)
634 |> assign(:user, non_admin_user)
635 |> assign(:token, token)
636 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
638 assert json_response(conn, :forbidden)
641 test "email with +", %{conn: conn, admin: admin} do
642 recipient_email = "foo+bar@baz.com"
645 |> put_req_header("content-type", "application/json;charset=utf-8")
646 |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
647 |> json_response(:no_content)
650 Pleroma.UserInviteToken
655 refute token_record.used
657 notify_email = Config.get([:instance, :notify_email])
658 instance_name = Config.get([:instance, :name])
661 Pleroma.Emails.UserEmail.user_invitation_email(
667 Swoosh.TestAssertions.assert_email_sent(
668 from: {instance_name, notify_email},
670 html_body: email.html_body
675 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
676 setup do: clear_config([:instance, :registrations_open])
677 setup do: clear_config([:instance, :invites_enabled])
679 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
680 Config.put([:instance, :registrations_open], false)
681 Config.put([:instance, :invites_enabled], false)
683 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
685 assert json_response(conn, :bad_request) ==
688 "To send invites you need to set the `invites_enabled` option to true."
692 test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
693 Config.put([:instance, :registrations_open], true)
694 Config.put([:instance, :invites_enabled], true)
696 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
698 assert json_response(conn, :bad_request) ==
701 "To send invites you need to set the `registrations_open` option to false."
706 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
711 |> put_req_header("accept", "application/json")
712 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
714 resp = json_response(conn, 200)
716 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
719 describe "GET /api/pleroma/admin/users" do
720 test "renders users array for the first page", %{conn: conn, admin: admin} do
721 user = insert(:user, local: false, tags: ["foo", "bar"])
722 conn = get(conn, "/api/pleroma/admin/users?page=1")
727 "deactivated" => admin.deactivated,
729 "nickname" => admin.nickname,
730 "roles" => %{"admin" => true, "moderator" => false},
733 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
734 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
735 "confirmation_pending" => false
738 "deactivated" => user.deactivated,
740 "nickname" => user.nickname,
741 "roles" => %{"admin" => false, "moderator" => false},
743 "tags" => ["foo", "bar"],
744 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
745 "display_name" => HTML.strip_tags(user.name || user.nickname),
746 "confirmation_pending" => false
749 |> Enum.sort_by(& &1["nickname"])
751 assert json_response(conn, 200) == %{
758 test "pagination works correctly with service users", %{conn: conn} do
759 service1 = insert(:user, ap_id: Web.base_url() <> "/relay")
760 service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch")
761 insert_list(25, :user)
763 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
765 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
766 |> json_response(200)
768 assert Enum.count(users1) == 10
769 assert service1 not in [users1]
770 assert service2 not in [users1]
772 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
774 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
775 |> json_response(200)
777 assert Enum.count(users2) == 10
778 assert service1 not in [users2]
779 assert service2 not in [users2]
781 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
783 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
784 |> json_response(200)
786 assert Enum.count(users3) == 6
787 assert service1 not in [users3]
788 assert service2 not in [users3]
791 test "renders empty array for the second page", %{conn: conn} do
794 conn = get(conn, "/api/pleroma/admin/users?page=2")
796 assert json_response(conn, 200) == %{
803 test "regular search", %{conn: conn} do
804 user = insert(:user, nickname: "bob")
806 conn = get(conn, "/api/pleroma/admin/users?query=bo")
808 assert json_response(conn, 200) == %{
813 "deactivated" => user.deactivated,
815 "nickname" => user.nickname,
816 "roles" => %{"admin" => false, "moderator" => false},
819 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
820 "display_name" => HTML.strip_tags(user.name || user.nickname),
821 "confirmation_pending" => false
827 test "search by domain", %{conn: conn} do
828 user = insert(:user, nickname: "nickname@domain.com")
831 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
833 assert json_response(conn, 200) == %{
838 "deactivated" => user.deactivated,
840 "nickname" => user.nickname,
841 "roles" => %{"admin" => false, "moderator" => false},
844 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
845 "display_name" => HTML.strip_tags(user.name || user.nickname),
846 "confirmation_pending" => false
852 test "search by full nickname", %{conn: conn} do
853 user = insert(:user, nickname: "nickname@domain.com")
856 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
858 assert json_response(conn, 200) == %{
863 "deactivated" => user.deactivated,
865 "nickname" => user.nickname,
866 "roles" => %{"admin" => false, "moderator" => false},
869 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
870 "display_name" => HTML.strip_tags(user.name || user.nickname),
871 "confirmation_pending" => false
877 test "search by display name", %{conn: conn} do
878 user = insert(:user, name: "Display name")
881 conn = get(conn, "/api/pleroma/admin/users?name=display")
883 assert json_response(conn, 200) == %{
888 "deactivated" => user.deactivated,
890 "nickname" => user.nickname,
891 "roles" => %{"admin" => false, "moderator" => false},
894 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
895 "display_name" => HTML.strip_tags(user.name || user.nickname),
896 "confirmation_pending" => false
902 test "search by email", %{conn: conn} do
903 user = insert(:user, email: "email@example.com")
906 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
908 assert json_response(conn, 200) == %{
913 "deactivated" => user.deactivated,
915 "nickname" => user.nickname,
916 "roles" => %{"admin" => false, "moderator" => false},
919 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
920 "display_name" => HTML.strip_tags(user.name || user.nickname),
921 "confirmation_pending" => false
927 test "regular search with page size", %{conn: conn} do
928 user = insert(:user, nickname: "aalice")
929 user2 = insert(:user, nickname: "alice")
931 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
933 assert json_response(conn1, 200) == %{
938 "deactivated" => user.deactivated,
940 "nickname" => user.nickname,
941 "roles" => %{"admin" => false, "moderator" => false},
944 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
945 "display_name" => HTML.strip_tags(user.name || user.nickname),
946 "confirmation_pending" => false
951 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
953 assert json_response(conn2, 200) == %{
958 "deactivated" => user2.deactivated,
960 "nickname" => user2.nickname,
961 "roles" => %{"admin" => false, "moderator" => false},
964 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
965 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
966 "confirmation_pending" => false
972 test "only local users" do
973 admin = insert(:user, is_admin: true, nickname: "john")
974 token = insert(:oauth_admin_token, user: admin)
975 user = insert(:user, nickname: "bob")
977 insert(:user, nickname: "bobb", local: false)
981 |> assign(:user, admin)
982 |> assign(:token, token)
983 |> get("/api/pleroma/admin/users?query=bo&filters=local")
985 assert json_response(conn, 200) == %{
990 "deactivated" => user.deactivated,
992 "nickname" => user.nickname,
993 "roles" => %{"admin" => false, "moderator" => false},
996 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
997 "display_name" => HTML.strip_tags(user.name || user.nickname),
998 "confirmation_pending" => false
1004 test "only local users with no query", %{conn: conn, admin: old_admin} do
1005 admin = insert(:user, is_admin: true, nickname: "john")
1006 user = insert(:user, nickname: "bob")
1008 insert(:user, nickname: "bobb", local: false)
1010 conn = get(conn, "/api/pleroma/admin/users?filters=local")
1015 "deactivated" => user.deactivated,
1017 "nickname" => user.nickname,
1018 "roles" => %{"admin" => false, "moderator" => false},
1021 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1022 "display_name" => HTML.strip_tags(user.name || user.nickname),
1023 "confirmation_pending" => false
1026 "deactivated" => admin.deactivated,
1028 "nickname" => admin.nickname,
1029 "roles" => %{"admin" => true, "moderator" => false},
1032 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1033 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1034 "confirmation_pending" => false
1037 "deactivated" => false,
1038 "id" => old_admin.id,
1040 "nickname" => old_admin.nickname,
1041 "roles" => %{"admin" => true, "moderator" => false},
1043 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
1044 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
1045 "confirmation_pending" => false
1048 |> Enum.sort_by(& &1["nickname"])
1050 assert json_response(conn, 200) == %{
1057 test "load only admins", %{conn: conn, admin: admin} do
1058 second_admin = insert(:user, is_admin: true)
1062 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
1067 "deactivated" => false,
1069 "nickname" => admin.nickname,
1070 "roles" => %{"admin" => true, "moderator" => false},
1071 "local" => admin.local,
1073 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1074 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1075 "confirmation_pending" => false
1078 "deactivated" => false,
1079 "id" => second_admin.id,
1080 "nickname" => second_admin.nickname,
1081 "roles" => %{"admin" => true, "moderator" => false},
1082 "local" => second_admin.local,
1084 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1085 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1086 "confirmation_pending" => false
1089 |> Enum.sort_by(& &1["nickname"])
1091 assert json_response(conn, 200) == %{
1098 test "load only moderators", %{conn: conn} do
1099 moderator = insert(:user, is_moderator: true)
1103 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1105 assert json_response(conn, 200) == %{
1110 "deactivated" => false,
1111 "id" => moderator.id,
1112 "nickname" => moderator.nickname,
1113 "roles" => %{"admin" => false, "moderator" => true},
1114 "local" => moderator.local,
1116 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1117 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1118 "confirmation_pending" => false
1124 test "load users with tags list", %{conn: conn} do
1125 user1 = insert(:user, tags: ["first"])
1126 user2 = insert(:user, tags: ["second"])
1130 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1135 "deactivated" => false,
1137 "nickname" => user1.nickname,
1138 "roles" => %{"admin" => false, "moderator" => false},
1139 "local" => user1.local,
1140 "tags" => ["first"],
1141 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1142 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1143 "confirmation_pending" => false
1146 "deactivated" => false,
1148 "nickname" => user2.nickname,
1149 "roles" => %{"admin" => false, "moderator" => false},
1150 "local" => user2.local,
1151 "tags" => ["second"],
1152 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1153 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1154 "confirmation_pending" => false
1157 |> Enum.sort_by(& &1["nickname"])
1159 assert json_response(conn, 200) == %{
1166 test "it works with multiple filters" do
1167 admin = insert(:user, nickname: "john", is_admin: true)
1168 token = insert(:oauth_admin_token, user: admin)
1169 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1171 insert(:user, nickname: "ken", local: true, deactivated: true)
1172 insert(:user, nickname: "bobb", local: false, deactivated: false)
1176 |> assign(:user, admin)
1177 |> assign(:token, token)
1178 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1180 assert json_response(conn, 200) == %{
1185 "deactivated" => user.deactivated,
1187 "nickname" => user.nickname,
1188 "roles" => %{"admin" => false, "moderator" => false},
1189 "local" => user.local,
1191 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1192 "display_name" => HTML.strip_tags(user.name || user.nickname),
1193 "confirmation_pending" => false
1199 test "it omits relay user", %{admin: admin, conn: conn} do
1200 assert %User{} = Relay.get_actor()
1202 conn = get(conn, "/api/pleroma/admin/users")
1204 assert json_response(conn, 200) == %{
1209 "deactivated" => admin.deactivated,
1211 "nickname" => admin.nickname,
1212 "roles" => %{"admin" => true, "moderator" => false},
1215 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1216 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1217 "confirmation_pending" => false
1224 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1225 user_one = insert(:user, deactivated: true)
1226 user_two = insert(:user, deactivated: true)
1231 "/api/pleroma/admin/users/activate",
1232 %{nicknames: [user_one.nickname, user_two.nickname]}
1235 response = json_response(conn, 200)
1236 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1238 log_entry = Repo.one(ModerationLog)
1240 assert ModerationLog.get_log_entry_message(log_entry) ==
1241 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1244 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1245 user_one = insert(:user, deactivated: false)
1246 user_two = insert(:user, deactivated: false)
1251 "/api/pleroma/admin/users/deactivate",
1252 %{nicknames: [user_one.nickname, user_two.nickname]}
1255 response = json_response(conn, 200)
1256 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1258 log_entry = Repo.one(ModerationLog)
1260 assert ModerationLog.get_log_entry_message(log_entry) ==
1261 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1264 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1265 user = insert(:user)
1267 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1269 assert json_response(conn, 200) ==
1271 "deactivated" => !user.deactivated,
1273 "nickname" => user.nickname,
1274 "roles" => %{"admin" => false, "moderator" => false},
1277 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1278 "display_name" => HTML.strip_tags(user.name || user.nickname),
1279 "confirmation_pending" => false
1282 log_entry = Repo.one(ModerationLog)
1284 assert ModerationLog.get_log_entry_message(log_entry) ==
1285 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1288 describe "PUT disable_mfa" do
1289 test "returns 200 and disable 2fa", %{conn: conn} do
1292 multi_factor_authentication_settings: %MFA.Settings{
1294 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1300 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1301 |> json_response(200)
1303 assert response == user.nickname
1304 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1306 refute mfa_settings.enabled
1307 refute mfa_settings.totp.confirmed
1310 test "returns 404 if user not found", %{conn: conn} do
1313 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1314 |> json_response(404)
1316 assert response == %{"error" => "Not found"}
1320 describe "POST /api/pleroma/admin/users/invite_token" do
1321 test "without options", %{conn: conn} do
1322 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1324 invite_json = json_response(conn, 200)
1325 invite = UserInviteToken.find_by_token!(invite_json["token"])
1327 refute invite.expires_at
1328 refute invite.max_use
1329 assert invite.invite_type == "one_time"
1332 test "with expires_at", %{conn: conn} do
1334 post(conn, "/api/pleroma/admin/users/invite_token", %{
1335 "expires_at" => Date.to_string(Date.utc_today())
1338 invite_json = json_response(conn, 200)
1339 invite = UserInviteToken.find_by_token!(invite_json["token"])
1342 assert invite.expires_at == Date.utc_today()
1343 refute invite.max_use
1344 assert invite.invite_type == "date_limited"
1347 test "with max_use", %{conn: conn} do
1348 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1350 invite_json = json_response(conn, 200)
1351 invite = UserInviteToken.find_by_token!(invite_json["token"])
1353 refute invite.expires_at
1354 assert invite.max_use == 150
1355 assert invite.invite_type == "reusable"
1358 test "with max use and expires_at", %{conn: conn} do
1360 post(conn, "/api/pleroma/admin/users/invite_token", %{
1362 "expires_at" => Date.to_string(Date.utc_today())
1365 invite_json = json_response(conn, 200)
1366 invite = UserInviteToken.find_by_token!(invite_json["token"])
1368 assert invite.expires_at == Date.utc_today()
1369 assert invite.max_use == 150
1370 assert invite.invite_type == "reusable_date_limited"
1374 describe "GET /api/pleroma/admin/users/invites" do
1375 test "no invites", %{conn: conn} do
1376 conn = get(conn, "/api/pleroma/admin/users/invites")
1378 assert json_response(conn, 200) == %{"invites" => []}
1381 test "with invite", %{conn: conn} do
1382 {:ok, invite} = UserInviteToken.create_invite()
1384 conn = get(conn, "/api/pleroma/admin/users/invites")
1386 assert json_response(conn, 200) == %{
1389 "expires_at" => nil,
1391 "invite_type" => "one_time",
1393 "token" => invite.token,
1402 describe "POST /api/pleroma/admin/users/revoke_invite" do
1403 test "with token", %{conn: conn} do
1404 {:ok, invite} = UserInviteToken.create_invite()
1406 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1408 assert json_response(conn, 200) == %{
1409 "expires_at" => nil,
1411 "invite_type" => "one_time",
1413 "token" => invite.token,
1419 test "with invalid token", %{conn: conn} do
1420 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1422 assert json_response(conn, :not_found) == %{"error" => "Not found"}
1426 describe "GET /api/pleroma/admin/reports/:id" do
1427 test "returns report by its id", %{conn: conn} do
1428 [reporter, target_user] = insert_pair(:user)
1429 activity = insert(:note_activity, user: target_user)
1431 {:ok, %{id: report_id}} =
1432 CommonAPI.report(reporter, %{
1433 account_id: target_user.id,
1434 comment: "I feel offended",
1435 status_ids: [activity.id]
1440 |> get("/api/pleroma/admin/reports/#{report_id}")
1441 |> json_response(:ok)
1443 assert response["id"] == report_id
1446 test "returns 404 when report id is invalid", %{conn: conn} do
1447 conn = get(conn, "/api/pleroma/admin/reports/test")
1449 assert json_response(conn, :not_found) == %{"error" => "Not found"}
1453 describe "PATCH /api/pleroma/admin/reports" do
1455 [reporter, target_user] = insert_pair(:user)
1456 activity = insert(:note_activity, user: target_user)
1458 {:ok, %{id: report_id}} =
1459 CommonAPI.report(reporter, %{
1460 account_id: target_user.id,
1461 comment: "I feel offended",
1462 status_ids: [activity.id]
1465 {:ok, %{id: second_report_id}} =
1466 CommonAPI.report(reporter, %{
1467 account_id: target_user.id,
1468 comment: "I feel very offended",
1469 status_ids: [activity.id]
1474 second_report_id: second_report_id
1478 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1479 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1480 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1484 |> assign(:token, read_token)
1485 |> patch("/api/pleroma/admin/reports", %{
1486 "reports" => [%{"state" => "resolved", "id" => id}]
1488 |> json_response(403)
1490 assert response == %{
1491 "error" => "Insufficient permissions: admin:write:reports."
1495 |> assign(:token, write_token)
1496 |> patch("/api/pleroma/admin/reports", %{
1497 "reports" => [%{"state" => "resolved", "id" => id}]
1499 |> json_response(:no_content)
1502 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1504 |> patch("/api/pleroma/admin/reports", %{
1506 %{"state" => "resolved", "id" => id}
1509 |> json_response(:no_content)
1511 activity = Activity.get_by_id(id)
1512 assert activity.data["state"] == "resolved"
1514 log_entry = Repo.one(ModerationLog)
1516 assert ModerationLog.get_log_entry_message(log_entry) ==
1517 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1520 test "closes report", %{conn: conn, id: id, admin: admin} do
1522 |> patch("/api/pleroma/admin/reports", %{
1524 %{"state" => "closed", "id" => id}
1527 |> json_response(:no_content)
1529 activity = Activity.get_by_id(id)
1530 assert activity.data["state"] == "closed"
1532 log_entry = Repo.one(ModerationLog)
1534 assert ModerationLog.get_log_entry_message(log_entry) ==
1535 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1538 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1541 |> patch("/api/pleroma/admin/reports", %{
1543 %{"state" => "test", "id" => id}
1547 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1550 test "returns 404 when report is not exist", %{conn: conn} do
1553 |> patch("/api/pleroma/admin/reports", %{
1555 %{"state" => "closed", "id" => "test"}
1559 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1562 test "updates state of multiple reports", %{
1566 second_report_id: second_report_id
1569 |> patch("/api/pleroma/admin/reports", %{
1571 %{"state" => "resolved", "id" => id},
1572 %{"state" => "closed", "id" => second_report_id}
1575 |> json_response(:no_content)
1577 activity = Activity.get_by_id(id)
1578 second_activity = Activity.get_by_id(second_report_id)
1579 assert activity.data["state"] == "resolved"
1580 assert second_activity.data["state"] == "closed"
1582 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1584 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1585 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1587 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1588 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1592 describe "GET /api/pleroma/admin/reports" do
1593 test "returns empty response when no reports created", %{conn: conn} do
1596 |> get("/api/pleroma/admin/reports")
1597 |> json_response(:ok)
1599 assert Enum.empty?(response["reports"])
1600 assert response["total"] == 0
1603 test "returns reports", %{conn: conn} do
1604 [reporter, target_user] = insert_pair(:user)
1605 activity = insert(:note_activity, user: target_user)
1607 {:ok, %{id: report_id}} =
1608 CommonAPI.report(reporter, %{
1609 account_id: target_user.id,
1610 comment: "I feel offended",
1611 status_ids: [activity.id]
1616 |> get("/api/pleroma/admin/reports")
1617 |> json_response(:ok)
1619 [report] = response["reports"]
1621 assert length(response["reports"]) == 1
1622 assert report["id"] == report_id
1624 assert response["total"] == 1
1627 test "returns reports with specified state", %{conn: conn} do
1628 [reporter, target_user] = insert_pair(:user)
1629 activity = insert(:note_activity, user: target_user)
1631 {:ok, %{id: first_report_id}} =
1632 CommonAPI.report(reporter, %{
1633 account_id: target_user.id,
1634 comment: "I feel offended",
1635 status_ids: [activity.id]
1638 {:ok, %{id: second_report_id}} =
1639 CommonAPI.report(reporter, %{
1640 account_id: target_user.id,
1641 comment: "I don't like this user"
1644 CommonAPI.update_report_state(second_report_id, "closed")
1648 |> get("/api/pleroma/admin/reports", %{
1651 |> json_response(:ok)
1653 [open_report] = response["reports"]
1655 assert length(response["reports"]) == 1
1656 assert open_report["id"] == first_report_id
1658 assert response["total"] == 1
1662 |> get("/api/pleroma/admin/reports", %{
1665 |> json_response(:ok)
1667 [closed_report] = response["reports"]
1669 assert length(response["reports"]) == 1
1670 assert closed_report["id"] == second_report_id
1672 assert response["total"] == 1
1676 |> get("/api/pleroma/admin/reports", %{
1677 "state" => "resolved"
1679 |> json_response(:ok)
1681 assert Enum.empty?(response["reports"])
1682 assert response["total"] == 0
1685 test "returns 403 when requested by a non-admin" do
1686 user = insert(:user)
1687 token = insert(:oauth_token, user: user)
1691 |> assign(:user, user)
1692 |> assign(:token, token)
1693 |> get("/api/pleroma/admin/reports")
1695 assert json_response(conn, :forbidden) ==
1696 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1699 test "returns 403 when requested by anonymous" do
1700 conn = get(build_conn(), "/api/pleroma/admin/reports")
1702 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1706 describe "GET /api/pleroma/admin/restart" do
1707 setup do: clear_config(:configurable_from_database, true)
1709 test "pleroma restarts", %{conn: conn} do
1711 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1712 end) =~ "pleroma restarted"
1714 refute Restarter.Pleroma.need_reboot?()
1718 test "need_reboot flag", %{conn: conn} do
1720 |> get("/api/pleroma/admin/need_reboot")
1721 |> json_response(200) == %{"need_reboot" => false}
1723 Restarter.Pleroma.need_reboot()
1726 |> get("/api/pleroma/admin/need_reboot")
1727 |> json_response(200) == %{"need_reboot" => true}
1729 on_exit(fn -> Restarter.Pleroma.refresh() end)
1732 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
1734 user = insert(:user)
1736 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1737 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1738 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1740 insert(:note_activity, user: user, published: date1)
1741 insert(:note_activity, user: user, published: date2)
1742 insert(:note_activity, user: user, published: date3)
1747 test "renders user's statuses", %{conn: conn, user: user} do
1748 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1750 assert json_response(conn, 200) |> length() == 3
1753 test "renders user's statuses with a limit", %{conn: conn, user: user} do
1754 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
1756 assert json_response(conn, 200) |> length() == 2
1759 test "doesn't return private statuses by default", %{conn: conn, user: user} do
1760 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1762 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1764 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1766 assert json_response(conn, 200) |> length() == 4
1769 test "returns private statuses with godmode on", %{conn: conn, user: user} do
1770 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1772 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1774 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
1776 assert json_response(conn, 200) |> length() == 5
1779 test "excludes reblogs by default", %{conn: conn, user: user} do
1780 other_user = insert(:user)
1781 {:ok, activity} = CommonAPI.post(user, %{status: "."})
1782 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
1784 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
1785 assert json_response(conn_res, 200) |> length() == 0
1788 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
1790 assert json_response(conn_res, 200) |> length() == 1
1794 describe "GET /api/pleroma/admin/moderation_log" do
1796 moderator = insert(:user, is_moderator: true)
1798 %{moderator: moderator}
1801 test "returns the log", %{conn: conn, admin: admin} do
1802 Repo.insert(%ModerationLog{
1806 "nickname" => admin.nickname,
1809 action: "relay_follow",
1810 target: "https://example.org/relay"
1812 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1815 Repo.insert(%ModerationLog{
1819 "nickname" => admin.nickname,
1822 action: "relay_unfollow",
1823 target: "https://example.org/relay"
1825 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1828 conn = get(conn, "/api/pleroma/admin/moderation_log")
1830 response = json_response(conn, 200)
1831 [first_entry, second_entry] = response["items"]
1833 assert response["total"] == 2
1834 assert first_entry["data"]["action"] == "relay_unfollow"
1836 assert first_entry["message"] ==
1837 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1839 assert second_entry["data"]["action"] == "relay_follow"
1841 assert second_entry["message"] ==
1842 "@#{admin.nickname} followed relay: https://example.org/relay"
1845 test "returns the log with pagination", %{conn: conn, admin: admin} do
1846 Repo.insert(%ModerationLog{
1850 "nickname" => admin.nickname,
1853 action: "relay_follow",
1854 target: "https://example.org/relay"
1856 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1859 Repo.insert(%ModerationLog{
1863 "nickname" => admin.nickname,
1866 action: "relay_unfollow",
1867 target: "https://example.org/relay"
1869 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1872 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
1874 response1 = json_response(conn1, 200)
1875 [first_entry] = response1["items"]
1877 assert response1["total"] == 2
1878 assert response1["items"] |> length() == 1
1879 assert first_entry["data"]["action"] == "relay_unfollow"
1881 assert first_entry["message"] ==
1882 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1884 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
1886 response2 = json_response(conn2, 200)
1887 [second_entry] = response2["items"]
1889 assert response2["total"] == 2
1890 assert response2["items"] |> length() == 1
1891 assert second_entry["data"]["action"] == "relay_follow"
1893 assert second_entry["message"] ==
1894 "@#{admin.nickname} followed relay: https://example.org/relay"
1897 test "filters log by date", %{conn: conn, admin: admin} do
1898 first_date = "2017-08-15T15:47:06Z"
1899 second_date = "2017-08-20T15:47:06Z"
1901 Repo.insert(%ModerationLog{
1905 "nickname" => admin.nickname,
1908 action: "relay_follow",
1909 target: "https://example.org/relay"
1911 inserted_at: NaiveDateTime.from_iso8601!(first_date)
1914 Repo.insert(%ModerationLog{
1918 "nickname" => admin.nickname,
1921 action: "relay_unfollow",
1922 target: "https://example.org/relay"
1924 inserted_at: NaiveDateTime.from_iso8601!(second_date)
1930 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
1933 response1 = json_response(conn1, 200)
1934 [first_entry] = response1["items"]
1936 assert response1["total"] == 1
1937 assert first_entry["data"]["action"] == "relay_unfollow"
1939 assert first_entry["message"] ==
1940 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1943 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
1944 Repo.insert(%ModerationLog{
1948 "nickname" => admin.nickname,
1951 action: "relay_follow",
1952 target: "https://example.org/relay"
1956 Repo.insert(%ModerationLog{
1959 "id" => moderator.id,
1960 "nickname" => moderator.nickname,
1963 action: "relay_unfollow",
1964 target: "https://example.org/relay"
1968 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
1970 response1 = json_response(conn1, 200)
1971 [first_entry] = response1["items"]
1973 assert response1["total"] == 1
1974 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
1977 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
1978 ModerationLog.insert_log(%{
1980 action: "relay_follow",
1981 target: "https://example.org/relay"
1984 ModerationLog.insert_log(%{
1986 action: "relay_unfollow",
1987 target: "https://example.org/relay"
1990 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
1992 response1 = json_response(conn1, 200)
1993 [first_entry] = response1["items"]
1995 assert response1["total"] == 1
1997 assert get_in(first_entry, ["data", "message"]) ==
1998 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
2002 describe "GET /users/:nickname/credentials" do
2003 test "gets the user credentials", %{conn: conn} do
2004 user = insert(:user)
2005 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
2007 response = assert json_response(conn, 200)
2008 assert response["email"] == user.email
2011 test "returns 403 if requested by a non-admin" do
2012 user = insert(:user)
2016 |> assign(:user, user)
2017 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
2019 assert json_response(conn, :forbidden)
2023 describe "PATCH /users/:nickname/credentials" do
2025 user = insert(:user)
2029 test "changes password and email", %{conn: conn, admin: admin, user: user} do
2030 assert user.password_reset_pending == false
2033 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2034 "password" => "new_password",
2035 "email" => "new_email@example.com",
2036 "name" => "new_name"
2039 assert json_response(conn, 200) == %{"status" => "success"}
2041 ObanHelpers.perform_all()
2043 updated_user = User.get_by_id(user.id)
2045 assert updated_user.email == "new_email@example.com"
2046 assert updated_user.name == "new_name"
2047 assert updated_user.password_hash != user.password_hash
2048 assert updated_user.password_reset_pending == true
2050 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
2052 assert ModerationLog.get_log_entry_message(log_entry1) ==
2053 "@#{admin.nickname} updated users: @#{user.nickname}"
2055 assert ModerationLog.get_log_entry_message(log_entry2) ==
2056 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
2059 test "returns 403 if requested by a non-admin", %{user: user} do
2062 |> assign(:user, user)
2063 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2064 "password" => "new_password",
2065 "email" => "new_email@example.com",
2066 "name" => "new_name"
2069 assert json_response(conn, :forbidden)
2072 test "changes actor type from permitted list", %{conn: conn, user: user} do
2073 assert user.actor_type == "Person"
2075 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2076 "actor_type" => "Service"
2078 |> json_response(200) == %{"status" => "success"}
2080 updated_user = User.get_by_id(user.id)
2082 assert updated_user.actor_type == "Service"
2084 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2085 "actor_type" => "Application"
2087 |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
2090 test "update non existing user", %{conn: conn} do
2091 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
2092 "password" => "new_password"
2094 |> json_response(200) == %{"error" => "Unable to update user."}
2098 describe "PATCH /users/:nickname/force_password_reset" do
2099 test "sets password_reset_pending to true", %{conn: conn} do
2100 user = insert(:user)
2101 assert user.password_reset_pending == false
2104 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
2106 assert json_response(conn, 204) == ""
2108 ObanHelpers.perform_all()
2110 assert User.get_by_id(user.id).password_reset_pending == true
2114 describe "relays" do
2115 test "POST /relay", %{conn: conn, admin: admin} do
2117 post(conn, "/api/pleroma/admin/relay", %{
2118 relay_url: "http://mastodon.example.org/users/admin"
2121 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2123 log_entry = Repo.one(ModerationLog)
2125 assert ModerationLog.get_log_entry_message(log_entry) ==
2126 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2129 test "GET /relay", %{conn: conn} do
2130 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
2132 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
2133 |> Enum.each(fn ap_id ->
2134 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
2135 User.follow(relay_user, user)
2138 conn = get(conn, "/api/pleroma/admin/relay")
2140 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
2143 test "DELETE /relay", %{conn: conn, admin: admin} do
2144 post(conn, "/api/pleroma/admin/relay", %{
2145 relay_url: "http://mastodon.example.org/users/admin"
2149 delete(conn, "/api/pleroma/admin/relay", %{
2150 relay_url: "http://mastodon.example.org/users/admin"
2153 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2155 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
2157 assert ModerationLog.get_log_entry_message(log_entry_one) ==
2158 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2160 assert ModerationLog.get_log_entry_message(log_entry_two) ==
2161 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
2165 describe "instances" do
2166 test "GET /instances/:instance/statuses", %{conn: conn} do
2167 user = insert(:user, local: false, nickname: "archaeme@archae.me")
2168 user2 = insert(:user, local: false, nickname: "test@test.com")
2169 insert_pair(:note_activity, user: user)
2170 activity = insert(:note_activity, user: user2)
2172 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
2174 response = json_response(ret_conn, 200)
2176 assert length(response) == 2
2178 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
2180 response = json_response(ret_conn, 200)
2182 assert length(response) == 1
2184 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
2186 response = json_response(ret_conn, 200)
2188 assert Enum.empty?(response)
2190 CommonAPI.repeat(activity.id, user)
2192 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
2193 response = json_response(ret_conn, 200)
2194 assert length(response) == 2
2196 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
2197 response = json_response(ret_conn, 200)
2198 assert length(response) == 3
2202 describe "PATCH /confirm_email" do
2203 test "it confirms emails of two users", %{conn: conn, admin: admin} do
2204 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2206 assert first_user.confirmation_pending == true
2207 assert second_user.confirmation_pending == true
2210 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
2212 first_user.nickname,
2213 second_user.nickname
2217 assert ret_conn.status == 200
2219 assert first_user.confirmation_pending == true
2220 assert second_user.confirmation_pending == true
2222 log_entry = Repo.one(ModerationLog)
2224 assert ModerationLog.get_log_entry_message(log_entry) ==
2225 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
2226 second_user.nickname
2231 describe "PATCH /resend_confirmation_email" do
2232 test "it resend emails for two users", %{conn: conn, admin: admin} do
2233 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2236 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
2238 first_user.nickname,
2239 second_user.nickname
2243 assert ret_conn.status == 200
2245 log_entry = Repo.one(ModerationLog)
2247 assert ModerationLog.get_log_entry_message(log_entry) ==
2248 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
2249 second_user.nickname
2254 describe "POST /reports/:id/notes" do
2255 setup %{conn: conn, admin: admin} do
2256 [reporter, target_user] = insert_pair(:user)
2257 activity = insert(:note_activity, user: target_user)
2259 {:ok, %{id: report_id}} =
2260 CommonAPI.report(reporter, %{
2261 account_id: target_user.id,
2262 comment: "I feel offended",
2263 status_ids: [activity.id]
2266 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
2267 content: "this is disgusting!"
2270 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
2271 content: "this is disgusting2!"
2276 report_id: report_id
2280 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
2281 [note, _] = Repo.all(ReportNote)
2284 activity_id: ^report_id,
2285 content: "this is disgusting!",
2290 test "it returns reports with notes", %{conn: conn, admin: admin} do
2291 conn = get(conn, "/api/pleroma/admin/reports")
2293 response = json_response(conn, 200)
2294 notes = hd(response["reports"])["notes"]
2297 assert note["user"]["nickname"] == admin.nickname
2298 assert note["content"] == "this is disgusting!"
2299 assert note["created_at"]
2300 assert response["total"] == 1
2303 test "it deletes the note", %{conn: conn, report_id: report_id} do
2304 assert ReportNote |> Repo.all() |> length() == 2
2306 [note, _] = Repo.all(ReportNote)
2308 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
2310 assert ReportNote |> Repo.all() |> length() == 1
2314 describe "/api/pleroma/admin/stats" do
2315 test "status visibility count", %{conn: conn} do
2316 admin = insert(:user, is_admin: true)
2317 user = insert(:user)
2318 CommonAPI.post(user, %{visibility: "public", status: "hey"})
2319 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
2320 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
2324 |> assign(:user, admin)
2325 |> get("/api/pleroma/admin/stats")
2326 |> json_response(200)
2328 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
2329 response["status_visibility"]
2333 describe "POST /api/pleroma/admin/oauth_app" do
2334 test "errors", %{conn: conn} do
2335 response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
2337 assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
2340 test "success", %{conn: conn} do
2341 base_url = Web.base_url()
2342 app_name = "Trusted app"
2346 |> post("/api/pleroma/admin/oauth_app", %{
2348 redirect_uris: base_url
2350 |> json_response(200)
2354 "client_secret" => _,
2355 "name" => ^app_name,
2356 "redirect_uri" => ^base_url,
2361 test "with trusted", %{conn: conn} do
2362 base_url = Web.base_url()
2363 app_name = "Trusted app"
2367 |> post("/api/pleroma/admin/oauth_app", %{
2369 redirect_uris: base_url,
2372 |> json_response(200)
2376 "client_secret" => _,
2377 "name" => ^app_name,
2378 "redirect_uri" => ^base_url,
2384 describe "GET /api/pleroma/admin/oauth_app" do
2386 app = insert(:oauth_app)
2390 test "list", %{conn: conn} do
2393 |> get("/api/pleroma/admin/oauth_app")
2394 |> json_response(200)
2396 assert %{"apps" => apps, "count" => count, "page_size" => _} = response
2398 assert length(apps) == count
2401 test "with page size", %{conn: conn} do
2407 |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
2408 |> json_response(200)
2410 assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
2412 assert length(apps) == page_size
2415 test "search by client name", %{conn: conn, app: app} do
2418 |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
2419 |> json_response(200)
2421 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
2423 assert returned["client_id"] == app.client_id
2424 assert returned["name"] == app.client_name
2427 test "search by client id", %{conn: conn, app: app} do
2430 |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
2431 |> json_response(200)
2433 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
2435 assert returned["client_id"] == app.client_id
2436 assert returned["name"] == app.client_name
2439 test "only trusted", %{conn: conn} do
2440 app = insert(:oauth_app, trusted: true)
2444 |> get("/api/pleroma/admin/oauth_app", %{trusted: true})
2445 |> json_response(200)
2447 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
2449 assert returned["client_id"] == app.client_id
2450 assert returned["name"] == app.client_name
2454 describe "DELETE /api/pleroma/admin/oauth_app/:id" do
2455 test "with id", %{conn: conn} do
2456 app = insert(:oauth_app)
2460 |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
2461 |> json_response(:no_content)
2463 assert response == ""
2466 test "with non existance id", %{conn: conn} do
2469 |> delete("/api/pleroma/admin/oauth_app/0")
2470 |> json_response(:bad_request)
2472 assert response == ""
2476 describe "PATCH /api/pleroma/admin/oauth_app/:id" do
2477 test "with id", %{conn: conn} do
2478 app = insert(:oauth_app)
2480 name = "another name"
2481 url = "https://example.com"
2484 website = "http://website.com"
2488 |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
2495 |> json_response(200)
2499 "client_secret" => _,
2502 "redirect_uri" => ^url,
2504 "website" => ^website
2508 test "without id", %{conn: conn} do
2511 |> patch("/api/pleroma/admin/oauth_app/0")
2512 |> json_response(:bad_request)
2514 assert response == ""
2519 # Needed for testing
2520 defmodule Pleroma.Web.Endpoint.NotReal do
2523 defmodule Pleroma.Captcha.NotReal do