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.Tests.ObanHelpers
22 alias Pleroma.Web.ActivityPub.Relay
23 alias Pleroma.Web.CommonAPI
24 alias Pleroma.Web.MediaProxy
27 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
33 admin = insert(:user, is_admin: true)
34 token = insert(:oauth_admin_token, user: admin)
38 |> assign(:user, admin)
39 |> assign(:token, token)
41 {:ok, %{admin: admin, token: token, conn: conn}}
44 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
45 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
47 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
50 url = "/api/pleroma/admin/users/#{user.nickname}"
52 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
53 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
54 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
56 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
57 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
60 for good_token <- [good_token1, good_token2, good_token3] do
63 |> assign(:user, admin)
64 |> assign(:token, good_token)
67 assert json_response(conn, 200)
70 for good_token <- [good_token1, good_token2, good_token3] do
74 |> assign(:token, good_token)
77 assert json_response(conn, :forbidden)
80 for bad_token <- [bad_token1, bad_token2, bad_token3] do
83 |> assign(:user, admin)
84 |> assign(:token, bad_token)
87 assert json_response(conn, :forbidden)
92 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
93 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
95 test "GET /api/pleroma/admin/users/:nickname requires " <>
96 "read:accounts or admin:read:accounts or broader scope",
99 url = "/api/pleroma/admin/users/#{user.nickname}"
101 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
102 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
103 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
104 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
105 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
107 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
109 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
110 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
113 for good_token <- good_tokens do
116 |> assign(:user, admin)
117 |> assign(:token, good_token)
120 assert json_response(conn, 200)
123 for good_token <- good_tokens do
126 |> assign(:user, nil)
127 |> assign(:token, good_token)
130 assert json_response(conn, :forbidden)
133 for bad_token <- [bad_token1, bad_token2, bad_token3] do
136 |> assign(:user, admin)
137 |> assign(:token, bad_token)
140 assert json_response(conn, :forbidden)
145 describe "DELETE /api/pleroma/admin/users" do
146 test "single user", %{admin: admin, conn: conn} do
148 clear_config([:instance, :federating], true)
150 with_mock Pleroma.Web.Federator,
151 publish: fn _ -> nil end do
154 |> put_req_header("accept", "application/json")
155 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
157 ObanHelpers.perform_all()
159 assert User.get_by_nickname(user.nickname).deactivated
161 log_entry = Repo.one(ModerationLog)
163 assert ModerationLog.get_log_entry_message(log_entry) ==
164 "@#{admin.nickname} deleted users: @#{user.nickname}"
166 assert json_response(conn, 200) == [user.nickname]
168 assert called(Pleroma.Web.Federator.publish(:_))
172 test "multiple users", %{admin: admin, conn: conn} do
173 user_one = insert(:user)
174 user_two = insert(:user)
178 |> put_req_header("accept", "application/json")
179 |> delete("/api/pleroma/admin/users", %{
180 nicknames: [user_one.nickname, user_two.nickname]
183 log_entry = Repo.one(ModerationLog)
185 assert ModerationLog.get_log_entry_message(log_entry) ==
186 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
188 response = json_response(conn, 200)
189 assert response -- [user_one.nickname, user_two.nickname] == []
193 describe "/api/pleroma/admin/users" do
194 test "Create", %{conn: conn} do
197 |> put_req_header("accept", "application/json")
198 |> post("/api/pleroma/admin/users", %{
201 "nickname" => "lain",
202 "email" => "lain@example.org",
206 "nickname" => "lain2",
207 "email" => "lain2@example.org",
213 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
214 assert response == ["success", "success"]
216 log_entry = Repo.one(ModerationLog)
218 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
221 test "Cannot create user with existing email", %{conn: conn} do
226 |> put_req_header("accept", "application/json")
227 |> post("/api/pleroma/admin/users", %{
230 "nickname" => "lain",
231 "email" => user.email,
237 assert json_response(conn, 409) == [
241 "email" => user.email,
244 "error" => "email has already been taken",
250 test "Cannot create user with existing nickname", %{conn: conn} do
255 |> put_req_header("accept", "application/json")
256 |> post("/api/pleroma/admin/users", %{
259 "nickname" => user.nickname,
260 "email" => "someuser@plerama.social",
266 assert json_response(conn, 409) == [
270 "email" => "someuser@plerama.social",
271 "nickname" => user.nickname
273 "error" => "nickname has already been taken",
279 test "Multiple user creation works in transaction", %{conn: conn} do
284 |> put_req_header("accept", "application/json")
285 |> post("/api/pleroma/admin/users", %{
288 "nickname" => "newuser",
289 "email" => "newuser@pleroma.social",
293 "nickname" => "lain",
294 "email" => user.email,
300 assert json_response(conn, 409) == [
304 "email" => user.email,
307 "error" => "email has already been taken",
313 "email" => "newuser@pleroma.social",
314 "nickname" => "newuser"
321 assert User.get_by_nickname("newuser") === nil
325 describe "/api/pleroma/admin/users/:nickname" do
326 test "Show", %{conn: conn} do
329 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
332 "deactivated" => false,
333 "id" => to_string(user.id),
335 "nickname" => user.nickname,
336 "roles" => %{"admin" => false, "moderator" => false},
338 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
339 "display_name" => HTML.strip_tags(user.name || user.nickname),
340 "confirmation_pending" => false,
342 "registration_reason" => nil
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 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
595 |> put_req_header("accept", "application/json")
596 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
598 resp = json_response(conn, 200)
600 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
603 describe "GET /api/pleroma/admin/users" do
604 test "renders users array for the first page", %{conn: conn, admin: admin} do
606 insert(:user, local: false, tags: ["foo", "bar"], registration_reason: "I'm a chill dude")
608 conn = get(conn, "/api/pleroma/admin/users?page=1")
613 "deactivated" => admin.deactivated,
615 "nickname" => admin.nickname,
616 "roles" => %{"admin" => true, "moderator" => false},
619 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
620 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
621 "confirmation_pending" => false,
622 "url" => admin.ap_id,
623 "registration_reason" => nil
626 "deactivated" => user.deactivated,
628 "nickname" => user.nickname,
629 "roles" => %{"admin" => false, "moderator" => false},
631 "tags" => ["foo", "bar"],
632 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
633 "display_name" => HTML.strip_tags(user.name || user.nickname),
634 "confirmation_pending" => false,
636 "registration_reason" => "I'm a chill dude"
639 |> Enum.sort_by(& &1["nickname"])
641 assert json_response(conn, 200) == %{
648 test "pagination works correctly with service users", %{conn: conn} do
649 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
651 insert_list(25, :user)
653 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
655 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
656 |> json_response(200)
658 assert Enum.count(users1) == 10
659 assert service1 not in users1
661 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
663 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
664 |> json_response(200)
666 assert Enum.count(users2) == 10
667 assert service1 not in users2
669 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
671 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
672 |> json_response(200)
674 assert Enum.count(users3) == 6
675 assert service1 not in users3
678 test "renders empty array for the second page", %{conn: conn} do
681 conn = get(conn, "/api/pleroma/admin/users?page=2")
683 assert json_response(conn, 200) == %{
690 test "regular search", %{conn: conn} do
691 user = insert(:user, nickname: "bob")
693 conn = get(conn, "/api/pleroma/admin/users?query=bo")
695 assert json_response(conn, 200) == %{
700 "deactivated" => user.deactivated,
702 "nickname" => user.nickname,
703 "roles" => %{"admin" => false, "moderator" => false},
706 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
707 "display_name" => HTML.strip_tags(user.name || user.nickname),
708 "confirmation_pending" => false,
710 "registration_reason" => nil
716 test "search by domain", %{conn: conn} do
717 user = insert(:user, nickname: "nickname@domain.com")
720 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
722 assert json_response(conn, 200) == %{
727 "deactivated" => user.deactivated,
729 "nickname" => user.nickname,
730 "roles" => %{"admin" => false, "moderator" => false},
733 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
734 "display_name" => HTML.strip_tags(user.name || user.nickname),
735 "confirmation_pending" => false,
737 "registration_reason" => nil
743 test "search by full nickname", %{conn: conn} do
744 user = insert(:user, nickname: "nickname@domain.com")
747 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
749 assert json_response(conn, 200) == %{
754 "deactivated" => user.deactivated,
756 "nickname" => user.nickname,
757 "roles" => %{"admin" => false, "moderator" => false},
760 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
761 "display_name" => HTML.strip_tags(user.name || user.nickname),
762 "confirmation_pending" => false,
764 "registration_reason" => nil
770 test "search by display name", %{conn: conn} do
771 user = insert(:user, name: "Display name")
774 conn = get(conn, "/api/pleroma/admin/users?name=display")
776 assert json_response(conn, 200) == %{
781 "deactivated" => user.deactivated,
783 "nickname" => user.nickname,
784 "roles" => %{"admin" => false, "moderator" => false},
787 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
788 "display_name" => HTML.strip_tags(user.name || user.nickname),
789 "confirmation_pending" => false,
791 "registration_reason" => nil
797 test "search by email", %{conn: conn} do
798 user = insert(:user, email: "email@example.com")
801 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
803 assert json_response(conn, 200) == %{
808 "deactivated" => user.deactivated,
810 "nickname" => user.nickname,
811 "roles" => %{"admin" => false, "moderator" => false},
814 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
815 "display_name" => HTML.strip_tags(user.name || user.nickname),
816 "confirmation_pending" => false,
818 "registration_reason" => nil
824 test "regular search with page size", %{conn: conn} do
825 user = insert(:user, nickname: "aalice")
826 user2 = insert(:user, nickname: "alice")
828 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
830 assert json_response(conn1, 200) == %{
835 "deactivated" => user.deactivated,
837 "nickname" => user.nickname,
838 "roles" => %{"admin" => false, "moderator" => false},
841 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
842 "display_name" => HTML.strip_tags(user.name || user.nickname),
843 "confirmation_pending" => false,
845 "registration_reason" => nil
850 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
852 assert json_response(conn2, 200) == %{
857 "deactivated" => user2.deactivated,
859 "nickname" => user2.nickname,
860 "roles" => %{"admin" => false, "moderator" => false},
863 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
864 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
865 "confirmation_pending" => false,
866 "url" => user2.ap_id,
867 "registration_reason" => nil
873 test "only local users" do
874 admin = insert(:user, is_admin: true, nickname: "john")
875 token = insert(:oauth_admin_token, user: admin)
876 user = insert(:user, nickname: "bob")
878 insert(:user, nickname: "bobb", local: false)
882 |> assign(:user, admin)
883 |> assign(:token, token)
884 |> get("/api/pleroma/admin/users?query=bo&filters=local")
886 assert json_response(conn, 200) == %{
891 "deactivated" => user.deactivated,
893 "nickname" => user.nickname,
894 "roles" => %{"admin" => false, "moderator" => false},
897 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
898 "display_name" => HTML.strip_tags(user.name || user.nickname),
899 "confirmation_pending" => false,
901 "registration_reason" => nil
907 test "only local users with no query", %{conn: conn, admin: old_admin} do
908 admin = insert(:user, is_admin: true, nickname: "john")
909 user = insert(:user, nickname: "bob")
911 insert(:user, nickname: "bobb", local: false)
913 conn = get(conn, "/api/pleroma/admin/users?filters=local")
918 "deactivated" => user.deactivated,
920 "nickname" => user.nickname,
921 "roles" => %{"admin" => false, "moderator" => false},
924 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
925 "display_name" => HTML.strip_tags(user.name || user.nickname),
926 "confirmation_pending" => false,
928 "registration_reason" => nil
931 "deactivated" => admin.deactivated,
933 "nickname" => admin.nickname,
934 "roles" => %{"admin" => true, "moderator" => false},
937 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
938 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
939 "confirmation_pending" => false,
940 "url" => admin.ap_id,
941 "registration_reason" => nil
944 "deactivated" => false,
945 "id" => old_admin.id,
947 "nickname" => old_admin.nickname,
948 "roles" => %{"admin" => true, "moderator" => false},
950 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
951 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
952 "confirmation_pending" => false,
953 "url" => old_admin.ap_id,
954 "registration_reason" => nil
957 |> Enum.sort_by(& &1["nickname"])
959 assert json_response(conn, 200) == %{
966 test "load only admins", %{conn: conn, admin: admin} do
967 second_admin = insert(:user, is_admin: true)
971 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
976 "deactivated" => false,
978 "nickname" => admin.nickname,
979 "roles" => %{"admin" => true, "moderator" => false},
980 "local" => admin.local,
982 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
983 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
984 "confirmation_pending" => false,
985 "url" => admin.ap_id,
986 "registration_reason" => nil
989 "deactivated" => false,
990 "id" => second_admin.id,
991 "nickname" => second_admin.nickname,
992 "roles" => %{"admin" => true, "moderator" => false},
993 "local" => second_admin.local,
995 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
996 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
997 "confirmation_pending" => false,
998 "url" => second_admin.ap_id,
999 "registration_reason" => nil
1002 |> Enum.sort_by(& &1["nickname"])
1004 assert json_response(conn, 200) == %{
1011 test "load only moderators", %{conn: conn} do
1012 moderator = insert(:user, is_moderator: true)
1016 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1018 assert json_response(conn, 200) == %{
1023 "deactivated" => false,
1024 "id" => moderator.id,
1025 "nickname" => moderator.nickname,
1026 "roles" => %{"admin" => false, "moderator" => true},
1027 "local" => moderator.local,
1029 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1030 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1031 "confirmation_pending" => false,
1032 "url" => moderator.ap_id,
1033 "registration_reason" => nil
1039 test "load users with tags list", %{conn: conn} do
1040 user1 = insert(:user, tags: ["first"])
1041 user2 = insert(:user, tags: ["second"])
1045 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1050 "deactivated" => false,
1052 "nickname" => user1.nickname,
1053 "roles" => %{"admin" => false, "moderator" => false},
1054 "local" => user1.local,
1055 "tags" => ["first"],
1056 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1057 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1058 "confirmation_pending" => false,
1059 "url" => user1.ap_id,
1060 "registration_reason" => nil
1063 "deactivated" => false,
1065 "nickname" => user2.nickname,
1066 "roles" => %{"admin" => false, "moderator" => false},
1067 "local" => user2.local,
1068 "tags" => ["second"],
1069 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1070 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1071 "confirmation_pending" => false,
1072 "url" => user2.ap_id,
1073 "registration_reason" => nil
1076 |> Enum.sort_by(& &1["nickname"])
1078 assert json_response(conn, 200) == %{
1085 test "it works with multiple filters" do
1086 admin = insert(:user, nickname: "john", is_admin: true)
1087 token = insert(:oauth_admin_token, user: admin)
1088 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1090 insert(:user, nickname: "ken", local: true, deactivated: true)
1091 insert(:user, nickname: "bobb", local: false, deactivated: false)
1095 |> assign(:user, admin)
1096 |> assign(:token, token)
1097 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1099 assert json_response(conn, 200) == %{
1104 "deactivated" => user.deactivated,
1106 "nickname" => user.nickname,
1107 "roles" => %{"admin" => false, "moderator" => false},
1108 "local" => user.local,
1110 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1111 "display_name" => HTML.strip_tags(user.name || user.nickname),
1112 "confirmation_pending" => false,
1113 "url" => user.ap_id,
1114 "registration_reason" => nil
1120 test "it omits relay user", %{admin: admin, conn: conn} do
1121 assert %User{} = Relay.get_actor()
1123 conn = get(conn, "/api/pleroma/admin/users")
1125 assert json_response(conn, 200) == %{
1130 "deactivated" => admin.deactivated,
1132 "nickname" => admin.nickname,
1133 "roles" => %{"admin" => true, "moderator" => false},
1136 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1137 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1138 "confirmation_pending" => false,
1139 "url" => admin.ap_id,
1140 "registration_reason" => nil
1147 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1148 user_one = insert(:user, deactivated: true)
1149 user_two = insert(:user, deactivated: true)
1154 "/api/pleroma/admin/users/activate",
1155 %{nicknames: [user_one.nickname, user_two.nickname]}
1158 response = json_response(conn, 200)
1159 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1161 log_entry = Repo.one(ModerationLog)
1163 assert ModerationLog.get_log_entry_message(log_entry) ==
1164 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1167 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1168 user_one = insert(:user, deactivated: false)
1169 user_two = insert(:user, deactivated: false)
1174 "/api/pleroma/admin/users/deactivate",
1175 %{nicknames: [user_one.nickname, user_two.nickname]}
1178 response = json_response(conn, 200)
1179 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1181 log_entry = Repo.one(ModerationLog)
1183 assert ModerationLog.get_log_entry_message(log_entry) ==
1184 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1187 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1188 user = insert(:user)
1190 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1192 assert json_response(conn, 200) ==
1194 "deactivated" => !user.deactivated,
1196 "nickname" => user.nickname,
1197 "roles" => %{"admin" => false, "moderator" => false},
1200 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1201 "display_name" => HTML.strip_tags(user.name || user.nickname),
1202 "confirmation_pending" => false,
1203 "url" => user.ap_id,
1204 "registration_reason" => nil
1207 log_entry = Repo.one(ModerationLog)
1209 assert ModerationLog.get_log_entry_message(log_entry) ==
1210 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1213 describe "PUT disable_mfa" do
1214 test "returns 200 and disable 2fa", %{conn: conn} do
1217 multi_factor_authentication_settings: %MFA.Settings{
1219 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1225 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1226 |> json_response(200)
1228 assert response == user.nickname
1229 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1231 refute mfa_settings.enabled
1232 refute mfa_settings.totp.confirmed
1235 test "returns 404 if user not found", %{conn: conn} do
1238 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1239 |> json_response(404)
1241 assert response == %{"error" => "Not found"}
1245 describe "GET /api/pleroma/admin/restart" do
1246 setup do: clear_config(:configurable_from_database, true)
1248 test "pleroma restarts", %{conn: conn} do
1250 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1251 end) =~ "pleroma restarted"
1253 refute Restarter.Pleroma.need_reboot?()
1257 test "need_reboot flag", %{conn: conn} do
1259 |> get("/api/pleroma/admin/need_reboot")
1260 |> json_response(200) == %{"need_reboot" => false}
1262 Restarter.Pleroma.need_reboot()
1265 |> get("/api/pleroma/admin/need_reboot")
1266 |> json_response(200) == %{"need_reboot" => true}
1268 on_exit(fn -> Restarter.Pleroma.refresh() end)
1271 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
1273 user = insert(:user)
1275 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1276 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1277 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1279 insert(:note_activity, user: user, published: date1)
1280 insert(:note_activity, user: user, published: date2)
1281 insert(:note_activity, user: user, published: date3)
1286 test "renders user's statuses", %{conn: conn, user: user} do
1287 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1289 assert json_response(conn, 200) |> length() == 3
1292 test "renders user's statuses with a limit", %{conn: conn, user: user} do
1293 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
1295 assert json_response(conn, 200) |> length() == 2
1298 test "doesn't return private statuses by default", %{conn: conn, user: user} do
1299 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1301 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1303 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1305 assert json_response(conn, 200) |> length() == 4
1308 test "returns private statuses with godmode on", %{conn: conn, user: user} do
1309 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1311 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1313 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
1315 assert json_response(conn, 200) |> length() == 5
1318 test "excludes reblogs by default", %{conn: conn, user: user} do
1319 other_user = insert(:user)
1320 {:ok, activity} = CommonAPI.post(user, %{status: "."})
1321 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
1323 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
1324 assert json_response(conn_res, 200) |> length() == 0
1327 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
1329 assert json_response(conn_res, 200) |> length() == 1
1333 describe "GET /api/pleroma/admin/moderation_log" do
1335 moderator = insert(:user, is_moderator: true)
1337 %{moderator: moderator}
1340 test "returns the log", %{conn: conn, admin: admin} do
1341 Repo.insert(%ModerationLog{
1345 "nickname" => admin.nickname,
1348 action: "relay_follow",
1349 target: "https://example.org/relay"
1351 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1354 Repo.insert(%ModerationLog{
1358 "nickname" => admin.nickname,
1361 action: "relay_unfollow",
1362 target: "https://example.org/relay"
1364 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1367 conn = get(conn, "/api/pleroma/admin/moderation_log")
1369 response = json_response(conn, 200)
1370 [first_entry, second_entry] = response["items"]
1372 assert response["total"] == 2
1373 assert first_entry["data"]["action"] == "relay_unfollow"
1375 assert first_entry["message"] ==
1376 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1378 assert second_entry["data"]["action"] == "relay_follow"
1380 assert second_entry["message"] ==
1381 "@#{admin.nickname} followed relay: https://example.org/relay"
1384 test "returns the log with pagination", %{conn: conn, admin: admin} do
1385 Repo.insert(%ModerationLog{
1389 "nickname" => admin.nickname,
1392 action: "relay_follow",
1393 target: "https://example.org/relay"
1395 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1398 Repo.insert(%ModerationLog{
1402 "nickname" => admin.nickname,
1405 action: "relay_unfollow",
1406 target: "https://example.org/relay"
1408 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1411 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
1413 response1 = json_response(conn1, 200)
1414 [first_entry] = response1["items"]
1416 assert response1["total"] == 2
1417 assert response1["items"] |> length() == 1
1418 assert first_entry["data"]["action"] == "relay_unfollow"
1420 assert first_entry["message"] ==
1421 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1423 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
1425 response2 = json_response(conn2, 200)
1426 [second_entry] = response2["items"]
1428 assert response2["total"] == 2
1429 assert response2["items"] |> length() == 1
1430 assert second_entry["data"]["action"] == "relay_follow"
1432 assert second_entry["message"] ==
1433 "@#{admin.nickname} followed relay: https://example.org/relay"
1436 test "filters log by date", %{conn: conn, admin: admin} do
1437 first_date = "2017-08-15T15:47:06Z"
1438 second_date = "2017-08-20T15:47:06Z"
1440 Repo.insert(%ModerationLog{
1444 "nickname" => admin.nickname,
1447 action: "relay_follow",
1448 target: "https://example.org/relay"
1450 inserted_at: NaiveDateTime.from_iso8601!(first_date)
1453 Repo.insert(%ModerationLog{
1457 "nickname" => admin.nickname,
1460 action: "relay_unfollow",
1461 target: "https://example.org/relay"
1463 inserted_at: NaiveDateTime.from_iso8601!(second_date)
1469 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
1472 response1 = json_response(conn1, 200)
1473 [first_entry] = response1["items"]
1475 assert response1["total"] == 1
1476 assert first_entry["data"]["action"] == "relay_unfollow"
1478 assert first_entry["message"] ==
1479 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1482 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
1483 Repo.insert(%ModerationLog{
1487 "nickname" => admin.nickname,
1490 action: "relay_follow",
1491 target: "https://example.org/relay"
1495 Repo.insert(%ModerationLog{
1498 "id" => moderator.id,
1499 "nickname" => moderator.nickname,
1502 action: "relay_unfollow",
1503 target: "https://example.org/relay"
1507 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
1509 response1 = json_response(conn1, 200)
1510 [first_entry] = response1["items"]
1512 assert response1["total"] == 1
1513 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
1516 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
1517 ModerationLog.insert_log(%{
1519 action: "relay_follow",
1520 target: "https://example.org/relay"
1523 ModerationLog.insert_log(%{
1525 action: "relay_unfollow",
1526 target: "https://example.org/relay"
1529 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
1531 response1 = json_response(conn1, 200)
1532 [first_entry] = response1["items"]
1534 assert response1["total"] == 1
1536 assert get_in(first_entry, ["data", "message"]) ==
1537 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
1541 test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated",
1543 clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated)
1544 user = insert(:user, %{local: false, nickname: "u@peer1.com"})
1545 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1547 assert json_response(conn, 200)
1550 describe "GET /users/:nickname/credentials" do
1551 test "gets the user credentials", %{conn: conn} do
1552 user = insert(:user)
1553 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1555 response = assert json_response(conn, 200)
1556 assert response["email"] == user.email
1559 test "returns 403 if requested by a non-admin" do
1560 user = insert(:user)
1564 |> assign(:user, user)
1565 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
1567 assert json_response(conn, :forbidden)
1571 describe "PATCH /users/:nickname/credentials" do
1573 user = insert(:user)
1577 test "changes password and email", %{conn: conn, admin: admin, user: user} do
1578 assert user.password_reset_pending == false
1581 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1582 "password" => "new_password",
1583 "email" => "new_email@example.com",
1584 "name" => "new_name"
1587 assert json_response(conn, 200) == %{"status" => "success"}
1589 ObanHelpers.perform_all()
1591 updated_user = User.get_by_id(user.id)
1593 assert updated_user.email == "new_email@example.com"
1594 assert updated_user.name == "new_name"
1595 assert updated_user.password_hash != user.password_hash
1596 assert updated_user.password_reset_pending == true
1598 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
1600 assert ModerationLog.get_log_entry_message(log_entry1) ==
1601 "@#{admin.nickname} updated users: @#{user.nickname}"
1603 assert ModerationLog.get_log_entry_message(log_entry2) ==
1604 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
1607 test "returns 403 if requested by a non-admin", %{user: user} do
1610 |> assign(:user, user)
1611 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1612 "password" => "new_password",
1613 "email" => "new_email@example.com",
1614 "name" => "new_name"
1617 assert json_response(conn, :forbidden)
1620 test "changes actor type from permitted list", %{conn: conn, user: user} do
1621 assert user.actor_type == "Person"
1623 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1624 "actor_type" => "Service"
1626 |> json_response(200) == %{"status" => "success"}
1628 updated_user = User.get_by_id(user.id)
1630 assert updated_user.actor_type == "Service"
1632 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1633 "actor_type" => "Application"
1635 |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}
1638 test "update non existing user", %{conn: conn} do
1639 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
1640 "password" => "new_password"
1642 |> json_response(404) == %{"error" => "Not found"}
1646 describe "PATCH /users/:nickname/force_password_reset" do
1647 test "sets password_reset_pending to true", %{conn: conn} do
1648 user = insert(:user)
1649 assert user.password_reset_pending == false
1652 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
1654 assert json_response(conn, 204) == ""
1656 ObanHelpers.perform_all()
1658 assert User.get_by_id(user.id).password_reset_pending == true
1662 describe "instances" do
1663 test "GET /instances/:instance/statuses", %{conn: conn} do
1664 user = insert(:user, local: false, nickname: "archaeme@archae.me")
1665 user2 = insert(:user, local: false, nickname: "test@test.com")
1666 insert_pair(:note_activity, user: user)
1667 activity = insert(:note_activity, user: user2)
1669 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1671 response = json_response(ret_conn, 200)
1673 assert length(response) == 2
1675 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
1677 response = json_response(ret_conn, 200)
1679 assert length(response) == 1
1681 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
1683 response = json_response(ret_conn, 200)
1685 assert Enum.empty?(response)
1687 CommonAPI.repeat(activity.id, user)
1689 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1690 response = json_response(ret_conn, 200)
1691 assert length(response) == 2
1693 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
1694 response = json_response(ret_conn, 200)
1695 assert length(response) == 3
1699 describe "PATCH /confirm_email" do
1700 test "it confirms emails of two users", %{conn: conn, admin: admin} do
1701 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1703 assert first_user.confirmation_pending == true
1704 assert second_user.confirmation_pending == true
1707 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
1709 first_user.nickname,
1710 second_user.nickname
1714 assert ret_conn.status == 200
1716 assert first_user.confirmation_pending == true
1717 assert second_user.confirmation_pending == true
1719 log_entry = Repo.one(ModerationLog)
1721 assert ModerationLog.get_log_entry_message(log_entry) ==
1722 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
1723 second_user.nickname
1728 describe "PATCH /resend_confirmation_email" do
1729 test "it resend emails for two users", %{conn: conn, admin: admin} do
1730 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1733 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
1735 first_user.nickname,
1736 second_user.nickname
1740 assert ret_conn.status == 200
1742 log_entry = Repo.one(ModerationLog)
1744 assert ModerationLog.get_log_entry_message(log_entry) ==
1745 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
1746 second_user.nickname
1751 describe "/api/pleroma/admin/stats" do
1752 test "status visibility count", %{conn: conn} do
1753 admin = insert(:user, is_admin: true)
1754 user = insert(:user)
1755 CommonAPI.post(user, %{visibility: "public", status: "hey"})
1756 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1757 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1761 |> assign(:user, admin)
1762 |> get("/api/pleroma/admin/stats")
1763 |> json_response(200)
1765 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
1766 response["status_visibility"]
1769 test "by instance", %{conn: conn} do
1770 admin = insert(:user, is_admin: true)
1771 user1 = insert(:user)
1772 instance2 = "instance2.tld"
1773 user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
1775 CommonAPI.post(user1, %{visibility: "public", status: "hey"})
1776 CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"})
1777 CommonAPI.post(user2, %{visibility: "private", status: "hey"})
1781 |> assign(:user, admin)
1782 |> get("/api/pleroma/admin/stats", instance: instance2)
1783 |> json_response(200)
1785 assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
1786 response["status_visibility"]
1791 # Needed for testing
1792 defmodule Pleroma.Web.Endpoint.NotReal do
1795 defmodule Pleroma.Captcha.NotReal do