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,
344 assert expected == json_response(conn, 200)
347 test "when the user doesn't exist", %{conn: conn} do
350 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
352 assert %{"error" => "Not found"} == json_response(conn, 404)
356 describe "/api/pleroma/admin/users/follow" do
357 test "allows to force-follow another user", %{admin: admin, conn: conn} do
359 follower = insert(:user)
362 |> put_req_header("accept", "application/json")
363 |> post("/api/pleroma/admin/users/follow", %{
364 "follower" => follower.nickname,
365 "followed" => user.nickname
368 user = User.get_cached_by_id(user.id)
369 follower = User.get_cached_by_id(follower.id)
371 assert User.following?(follower, user)
373 log_entry = Repo.one(ModerationLog)
375 assert ModerationLog.get_log_entry_message(log_entry) ==
376 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
380 describe "/api/pleroma/admin/users/unfollow" do
381 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
383 follower = insert(:user)
385 User.follow(follower, user)
388 |> put_req_header("accept", "application/json")
389 |> post("/api/pleroma/admin/users/unfollow", %{
390 "follower" => follower.nickname,
391 "followed" => user.nickname
394 user = User.get_cached_by_id(user.id)
395 follower = User.get_cached_by_id(follower.id)
397 refute User.following?(follower, user)
399 log_entry = Repo.one(ModerationLog)
401 assert ModerationLog.get_log_entry_message(log_entry) ==
402 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
406 describe "PUT /api/pleroma/admin/users/tag" do
407 setup %{conn: conn} do
408 user1 = insert(:user, %{tags: ["x"]})
409 user2 = insert(:user, %{tags: ["y"]})
410 user3 = insert(:user, %{tags: ["unchanged"]})
414 |> put_req_header("accept", "application/json")
416 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
417 "#{user2.nickname}&tags[]=foo&tags[]=bar"
420 %{conn: conn, user1: user1, user2: user2, user3: user3}
423 test "it appends specified tags to users with specified nicknames", %{
429 assert json_response(conn, :no_content)
430 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
431 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
433 log_entry = Repo.one(ModerationLog)
436 [user1.nickname, user2.nickname]
437 |> Enum.map(&"@#{&1}")
440 tags = ["foo", "bar"] |> Enum.join(", ")
442 assert ModerationLog.get_log_entry_message(log_entry) ==
443 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
446 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
447 assert json_response(conn, :no_content)
448 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
452 describe "DELETE /api/pleroma/admin/users/tag" do
453 setup %{conn: conn} do
454 user1 = insert(:user, %{tags: ["x"]})
455 user2 = insert(:user, %{tags: ["y", "z"]})
456 user3 = insert(:user, %{tags: ["unchanged"]})
460 |> put_req_header("accept", "application/json")
462 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
463 "#{user2.nickname}&tags[]=x&tags[]=z"
466 %{conn: conn, user1: user1, user2: user2, user3: user3}
469 test "it removes specified tags from users with specified nicknames", %{
475 assert json_response(conn, :no_content)
476 assert User.get_cached_by_id(user1.id).tags == []
477 assert User.get_cached_by_id(user2.id).tags == ["y"]
479 log_entry = Repo.one(ModerationLog)
482 [user1.nickname, user2.nickname]
483 |> Enum.map(&"@#{&1}")
486 tags = ["x", "z"] |> Enum.join(", ")
488 assert ModerationLog.get_log_entry_message(log_entry) ==
489 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
492 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
493 assert json_response(conn, :no_content)
494 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
498 describe "/api/pleroma/admin/users/:nickname/permission_group" do
499 test "GET is giving user_info", %{admin: admin, conn: conn} do
502 |> put_req_header("accept", "application/json")
503 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
505 assert json_response(conn, 200) == %{
507 "is_moderator" => false
511 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
516 |> put_req_header("accept", "application/json")
517 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
519 assert json_response(conn, 200) == %{
523 log_entry = Repo.one(ModerationLog)
525 assert ModerationLog.get_log_entry_message(log_entry) ==
526 "@#{admin.nickname} made @#{user.nickname} admin"
529 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
530 user_one = insert(:user)
531 user_two = insert(:user)
535 |> put_req_header("accept", "application/json")
536 |> post("/api/pleroma/admin/users/permission_group/admin", %{
537 nicknames: [user_one.nickname, user_two.nickname]
540 assert json_response(conn, 200) == %{"is_admin" => true}
542 log_entry = Repo.one(ModerationLog)
544 assert ModerationLog.get_log_entry_message(log_entry) ==
545 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
548 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
549 user = insert(:user, is_admin: true)
553 |> put_req_header("accept", "application/json")
554 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
556 assert json_response(conn, 200) == %{"is_admin" => false}
558 log_entry = Repo.one(ModerationLog)
560 assert ModerationLog.get_log_entry_message(log_entry) ==
561 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
564 test "/:right DELETE, can remove from a permission group (multiple)", %{
568 user_one = insert(:user, is_admin: true)
569 user_two = insert(:user, is_admin: true)
573 |> put_req_header("accept", "application/json")
574 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
575 nicknames: [user_one.nickname, user_two.nickname]
578 assert json_response(conn, 200) == %{"is_admin" => false}
580 log_entry = Repo.one(ModerationLog)
582 assert ModerationLog.get_log_entry_message(log_entry) ==
583 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
589 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
594 |> put_req_header("accept", "application/json")
595 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
597 resp = json_response(conn, 200)
599 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
602 describe "GET /api/pleroma/admin/users" do
603 test "renders users array for the first page", %{conn: conn, admin: admin} do
604 user = insert(:user, local: false, tags: ["foo", "bar"])
605 conn = get(conn, "/api/pleroma/admin/users?page=1")
610 "deactivated" => admin.deactivated,
612 "nickname" => admin.nickname,
613 "roles" => %{"admin" => true, "moderator" => false},
616 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
617 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
618 "confirmation_pending" => false,
622 "deactivated" => user.deactivated,
624 "nickname" => user.nickname,
625 "roles" => %{"admin" => false, "moderator" => false},
627 "tags" => ["foo", "bar"],
628 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
629 "display_name" => HTML.strip_tags(user.name || user.nickname),
630 "confirmation_pending" => false,
634 |> Enum.sort_by(& &1["nickname"])
636 assert json_response(conn, 200) == %{
643 test "pagination works correctly with service users", %{conn: conn} do
644 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
646 insert_list(25, :user)
648 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
650 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
651 |> json_response(200)
653 assert Enum.count(users1) == 10
654 assert service1 not in users1
656 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
658 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
659 |> json_response(200)
661 assert Enum.count(users2) == 10
662 assert service1 not in users2
664 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
666 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
667 |> json_response(200)
669 assert Enum.count(users3) == 6
670 assert service1 not in users3
673 test "renders empty array for the second page", %{conn: conn} do
676 conn = get(conn, "/api/pleroma/admin/users?page=2")
678 assert json_response(conn, 200) == %{
685 test "regular search", %{conn: conn} do
686 user = insert(:user, nickname: "bob")
688 conn = get(conn, "/api/pleroma/admin/users?query=bo")
690 assert json_response(conn, 200) == %{
695 "deactivated" => user.deactivated,
697 "nickname" => user.nickname,
698 "roles" => %{"admin" => false, "moderator" => false},
701 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
702 "display_name" => HTML.strip_tags(user.name || user.nickname),
703 "confirmation_pending" => false,
710 test "search by domain", %{conn: conn} do
711 user = insert(:user, nickname: "nickname@domain.com")
714 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
716 assert json_response(conn, 200) == %{
721 "deactivated" => user.deactivated,
723 "nickname" => user.nickname,
724 "roles" => %{"admin" => false, "moderator" => false},
727 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
728 "display_name" => HTML.strip_tags(user.name || user.nickname),
729 "confirmation_pending" => false,
736 test "search by full nickname", %{conn: conn} do
737 user = insert(:user, nickname: "nickname@domain.com")
740 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
742 assert json_response(conn, 200) == %{
747 "deactivated" => user.deactivated,
749 "nickname" => user.nickname,
750 "roles" => %{"admin" => false, "moderator" => false},
753 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
754 "display_name" => HTML.strip_tags(user.name || user.nickname),
755 "confirmation_pending" => false,
762 test "search by display name", %{conn: conn} do
763 user = insert(:user, name: "Display name")
766 conn = get(conn, "/api/pleroma/admin/users?name=display")
768 assert json_response(conn, 200) == %{
773 "deactivated" => user.deactivated,
775 "nickname" => user.nickname,
776 "roles" => %{"admin" => false, "moderator" => false},
779 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
780 "display_name" => HTML.strip_tags(user.name || user.nickname),
781 "confirmation_pending" => false,
788 test "search by email", %{conn: conn} do
789 user = insert(:user, email: "email@example.com")
792 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
794 assert json_response(conn, 200) == %{
799 "deactivated" => user.deactivated,
801 "nickname" => user.nickname,
802 "roles" => %{"admin" => false, "moderator" => false},
805 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
806 "display_name" => HTML.strip_tags(user.name || user.nickname),
807 "confirmation_pending" => false,
814 test "regular search with page size", %{conn: conn} do
815 user = insert(:user, nickname: "aalice")
816 user2 = insert(:user, nickname: "alice")
818 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
820 assert json_response(conn1, 200) == %{
825 "deactivated" => user.deactivated,
827 "nickname" => user.nickname,
828 "roles" => %{"admin" => false, "moderator" => false},
831 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
832 "display_name" => HTML.strip_tags(user.name || user.nickname),
833 "confirmation_pending" => false,
839 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
841 assert json_response(conn2, 200) == %{
846 "deactivated" => user2.deactivated,
848 "nickname" => user2.nickname,
849 "roles" => %{"admin" => false, "moderator" => false},
852 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
853 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
854 "confirmation_pending" => false,
861 test "only local users" do
862 admin = insert(:user, is_admin: true, nickname: "john")
863 token = insert(:oauth_admin_token, user: admin)
864 user = insert(:user, nickname: "bob")
866 insert(:user, nickname: "bobb", local: false)
870 |> assign(:user, admin)
871 |> assign(:token, token)
872 |> get("/api/pleroma/admin/users?query=bo&filters=local")
874 assert json_response(conn, 200) == %{
879 "deactivated" => user.deactivated,
881 "nickname" => user.nickname,
882 "roles" => %{"admin" => false, "moderator" => false},
885 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
886 "display_name" => HTML.strip_tags(user.name || user.nickname),
887 "confirmation_pending" => false,
894 test "only local users with no query", %{conn: conn, admin: old_admin} do
895 admin = insert(:user, is_admin: true, nickname: "john")
896 user = insert(:user, nickname: "bob")
898 insert(:user, nickname: "bobb", local: false)
900 conn = get(conn, "/api/pleroma/admin/users?filters=local")
905 "deactivated" => user.deactivated,
907 "nickname" => user.nickname,
908 "roles" => %{"admin" => false, "moderator" => false},
911 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
912 "display_name" => HTML.strip_tags(user.name || user.nickname),
913 "confirmation_pending" => false,
917 "deactivated" => admin.deactivated,
919 "nickname" => admin.nickname,
920 "roles" => %{"admin" => true, "moderator" => false},
923 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
924 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
925 "confirmation_pending" => false,
929 "deactivated" => false,
930 "id" => old_admin.id,
932 "nickname" => old_admin.nickname,
933 "roles" => %{"admin" => true, "moderator" => false},
935 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
936 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
937 "confirmation_pending" => false,
938 "url" => old_admin.ap_id
941 |> Enum.sort_by(& &1["nickname"])
943 assert json_response(conn, 200) == %{
950 test "load only admins", %{conn: conn, admin: admin} do
951 second_admin = insert(:user, is_admin: true)
955 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
960 "deactivated" => false,
962 "nickname" => admin.nickname,
963 "roles" => %{"admin" => true, "moderator" => false},
964 "local" => admin.local,
966 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
967 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
968 "confirmation_pending" => false,
972 "deactivated" => false,
973 "id" => second_admin.id,
974 "nickname" => second_admin.nickname,
975 "roles" => %{"admin" => true, "moderator" => false},
976 "local" => second_admin.local,
978 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
979 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
980 "confirmation_pending" => false,
981 "url" => second_admin.ap_id
984 |> Enum.sort_by(& &1["nickname"])
986 assert json_response(conn, 200) == %{
993 test "load only moderators", %{conn: conn} do
994 moderator = insert(:user, is_moderator: true)
998 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1000 assert json_response(conn, 200) == %{
1005 "deactivated" => false,
1006 "id" => moderator.id,
1007 "nickname" => moderator.nickname,
1008 "roles" => %{"admin" => false, "moderator" => true},
1009 "local" => moderator.local,
1011 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1012 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1013 "confirmation_pending" => false,
1014 "url" => moderator.ap_id
1020 test "load users with tags list", %{conn: conn} do
1021 user1 = insert(:user, tags: ["first"])
1022 user2 = insert(:user, tags: ["second"])
1026 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1031 "deactivated" => false,
1033 "nickname" => user1.nickname,
1034 "roles" => %{"admin" => false, "moderator" => false},
1035 "local" => user1.local,
1036 "tags" => ["first"],
1037 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1038 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1039 "confirmation_pending" => false,
1040 "url" => user1.ap_id
1043 "deactivated" => false,
1045 "nickname" => user2.nickname,
1046 "roles" => %{"admin" => false, "moderator" => false},
1047 "local" => user2.local,
1048 "tags" => ["second"],
1049 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1050 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1051 "confirmation_pending" => false,
1052 "url" => user2.ap_id
1055 |> Enum.sort_by(& &1["nickname"])
1057 assert json_response(conn, 200) == %{
1064 test "it works with multiple filters" do
1065 admin = insert(:user, nickname: "john", is_admin: true)
1066 token = insert(:oauth_admin_token, user: admin)
1067 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1069 insert(:user, nickname: "ken", local: true, deactivated: true)
1070 insert(:user, nickname: "bobb", local: false, deactivated: false)
1074 |> assign(:user, admin)
1075 |> assign(:token, token)
1076 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1078 assert json_response(conn, 200) == %{
1083 "deactivated" => user.deactivated,
1085 "nickname" => user.nickname,
1086 "roles" => %{"admin" => false, "moderator" => false},
1087 "local" => user.local,
1089 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1090 "display_name" => HTML.strip_tags(user.name || user.nickname),
1091 "confirmation_pending" => false,
1098 test "it omits relay user", %{admin: admin, conn: conn} do
1099 assert %User{} = Relay.get_actor()
1101 conn = get(conn, "/api/pleroma/admin/users")
1103 assert json_response(conn, 200) == %{
1108 "deactivated" => admin.deactivated,
1110 "nickname" => admin.nickname,
1111 "roles" => %{"admin" => true, "moderator" => false},
1114 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1115 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1116 "confirmation_pending" => false,
1117 "url" => admin.ap_id
1124 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1125 user_one = insert(:user, deactivated: true)
1126 user_two = insert(:user, deactivated: true)
1131 "/api/pleroma/admin/users/activate",
1132 %{nicknames: [user_one.nickname, user_two.nickname]}
1135 response = json_response(conn, 200)
1136 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1138 log_entry = Repo.one(ModerationLog)
1140 assert ModerationLog.get_log_entry_message(log_entry) ==
1141 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1144 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1145 user_one = insert(:user, deactivated: false)
1146 user_two = insert(:user, deactivated: false)
1151 "/api/pleroma/admin/users/deactivate",
1152 %{nicknames: [user_one.nickname, user_two.nickname]}
1155 response = json_response(conn, 200)
1156 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1158 log_entry = Repo.one(ModerationLog)
1160 assert ModerationLog.get_log_entry_message(log_entry) ==
1161 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1164 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1165 user = insert(:user)
1167 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1169 assert json_response(conn, 200) ==
1171 "deactivated" => !user.deactivated,
1173 "nickname" => user.nickname,
1174 "roles" => %{"admin" => false, "moderator" => false},
1177 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1178 "display_name" => HTML.strip_tags(user.name || user.nickname),
1179 "confirmation_pending" => false,
1183 log_entry = Repo.one(ModerationLog)
1185 assert ModerationLog.get_log_entry_message(log_entry) ==
1186 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1189 describe "PUT disable_mfa" do
1190 test "returns 200 and disable 2fa", %{conn: conn} do
1193 multi_factor_authentication_settings: %MFA.Settings{
1195 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1201 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1202 |> json_response(200)
1204 assert response == user.nickname
1205 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1207 refute mfa_settings.enabled
1208 refute mfa_settings.totp.confirmed
1211 test "returns 404 if user not found", %{conn: conn} do
1214 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1215 |> json_response(404)
1217 assert response == %{"error" => "Not found"}
1221 describe "GET /api/pleroma/admin/restart" do
1222 setup do: clear_config(:configurable_from_database, true)
1224 test "pleroma restarts", %{conn: conn} do
1226 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1227 end) =~ "pleroma restarted"
1229 refute Restarter.Pleroma.need_reboot?()
1233 test "need_reboot flag", %{conn: conn} do
1235 |> get("/api/pleroma/admin/need_reboot")
1236 |> json_response(200) == %{"need_reboot" => false}
1238 Restarter.Pleroma.need_reboot()
1241 |> get("/api/pleroma/admin/need_reboot")
1242 |> json_response(200) == %{"need_reboot" => true}
1244 on_exit(fn -> Restarter.Pleroma.refresh() end)
1247 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
1249 user = insert(:user)
1251 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1252 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1253 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1255 insert(:note_activity, user: user, published: date1)
1256 insert(:note_activity, user: user, published: date2)
1257 insert(:note_activity, user: user, published: date3)
1262 test "renders user's statuses", %{conn: conn, user: user} do
1263 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1265 assert json_response(conn, 200) |> length() == 3
1268 test "renders user's statuses with a limit", %{conn: conn, user: user} do
1269 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
1271 assert json_response(conn, 200) |> length() == 2
1274 test "doesn't return private statuses by default", %{conn: conn, user: user} do
1275 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1277 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1279 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1281 assert json_response(conn, 200) |> length() == 4
1284 test "returns private statuses with godmode on", %{conn: conn, user: user} do
1285 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1287 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1289 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
1291 assert json_response(conn, 200) |> length() == 5
1294 test "excludes reblogs by default", %{conn: conn, user: user} do
1295 other_user = insert(:user)
1296 {:ok, activity} = CommonAPI.post(user, %{status: "."})
1297 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
1299 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
1300 assert json_response(conn_res, 200) |> length() == 0
1303 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
1305 assert json_response(conn_res, 200) |> length() == 1
1309 describe "GET /api/pleroma/admin/moderation_log" do
1311 moderator = insert(:user, is_moderator: true)
1313 %{moderator: moderator}
1316 test "returns the log", %{conn: conn, admin: admin} do
1317 Repo.insert(%ModerationLog{
1321 "nickname" => admin.nickname,
1324 action: "relay_follow",
1325 target: "https://example.org/relay"
1327 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1330 Repo.insert(%ModerationLog{
1334 "nickname" => admin.nickname,
1337 action: "relay_unfollow",
1338 target: "https://example.org/relay"
1340 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1343 conn = get(conn, "/api/pleroma/admin/moderation_log")
1345 response = json_response(conn, 200)
1346 [first_entry, second_entry] = response["items"]
1348 assert response["total"] == 2
1349 assert first_entry["data"]["action"] == "relay_unfollow"
1351 assert first_entry["message"] ==
1352 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1354 assert second_entry["data"]["action"] == "relay_follow"
1356 assert second_entry["message"] ==
1357 "@#{admin.nickname} followed relay: https://example.org/relay"
1360 test "returns the log with pagination", %{conn: conn, admin: admin} do
1361 Repo.insert(%ModerationLog{
1365 "nickname" => admin.nickname,
1368 action: "relay_follow",
1369 target: "https://example.org/relay"
1371 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1374 Repo.insert(%ModerationLog{
1378 "nickname" => admin.nickname,
1381 action: "relay_unfollow",
1382 target: "https://example.org/relay"
1384 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1387 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
1389 response1 = json_response(conn1, 200)
1390 [first_entry] = response1["items"]
1392 assert response1["total"] == 2
1393 assert response1["items"] |> length() == 1
1394 assert first_entry["data"]["action"] == "relay_unfollow"
1396 assert first_entry["message"] ==
1397 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1399 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
1401 response2 = json_response(conn2, 200)
1402 [second_entry] = response2["items"]
1404 assert response2["total"] == 2
1405 assert response2["items"] |> length() == 1
1406 assert second_entry["data"]["action"] == "relay_follow"
1408 assert second_entry["message"] ==
1409 "@#{admin.nickname} followed relay: https://example.org/relay"
1412 test "filters log by date", %{conn: conn, admin: admin} do
1413 first_date = "2017-08-15T15:47:06Z"
1414 second_date = "2017-08-20T15:47:06Z"
1416 Repo.insert(%ModerationLog{
1420 "nickname" => admin.nickname,
1423 action: "relay_follow",
1424 target: "https://example.org/relay"
1426 inserted_at: NaiveDateTime.from_iso8601!(first_date)
1429 Repo.insert(%ModerationLog{
1433 "nickname" => admin.nickname,
1436 action: "relay_unfollow",
1437 target: "https://example.org/relay"
1439 inserted_at: NaiveDateTime.from_iso8601!(second_date)
1445 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
1448 response1 = json_response(conn1, 200)
1449 [first_entry] = response1["items"]
1451 assert response1["total"] == 1
1452 assert first_entry["data"]["action"] == "relay_unfollow"
1454 assert first_entry["message"] ==
1455 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1458 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
1459 Repo.insert(%ModerationLog{
1463 "nickname" => admin.nickname,
1466 action: "relay_follow",
1467 target: "https://example.org/relay"
1471 Repo.insert(%ModerationLog{
1474 "id" => moderator.id,
1475 "nickname" => moderator.nickname,
1478 action: "relay_unfollow",
1479 target: "https://example.org/relay"
1483 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
1485 response1 = json_response(conn1, 200)
1486 [first_entry] = response1["items"]
1488 assert response1["total"] == 1
1489 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
1492 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
1493 ModerationLog.insert_log(%{
1495 action: "relay_follow",
1496 target: "https://example.org/relay"
1499 ModerationLog.insert_log(%{
1501 action: "relay_unfollow",
1502 target: "https://example.org/relay"
1505 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
1507 response1 = json_response(conn1, 200)
1508 [first_entry] = response1["items"]
1510 assert response1["total"] == 1
1512 assert get_in(first_entry, ["data", "message"]) ==
1513 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
1517 describe "GET /users/:nickname/credentials" do
1518 test "gets the user credentials", %{conn: conn} do
1519 user = insert(:user)
1520 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1522 response = assert json_response(conn, 200)
1523 assert response["email"] == user.email
1526 test "returns 403 if requested by a non-admin" do
1527 user = insert(:user)
1531 |> assign(:user, user)
1532 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
1534 assert json_response(conn, :forbidden)
1538 describe "PATCH /users/:nickname/credentials" do
1540 user = insert(:user)
1544 test "changes password and email", %{conn: conn, admin: admin, user: user} do
1545 assert user.password_reset_pending == false
1548 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1549 "password" => "new_password",
1550 "email" => "new_email@example.com",
1551 "name" => "new_name"
1554 assert json_response(conn, 200) == %{"status" => "success"}
1556 ObanHelpers.perform_all()
1558 updated_user = User.get_by_id(user.id)
1560 assert updated_user.email == "new_email@example.com"
1561 assert updated_user.name == "new_name"
1562 assert updated_user.password_hash != user.password_hash
1563 assert updated_user.password_reset_pending == true
1565 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
1567 assert ModerationLog.get_log_entry_message(log_entry1) ==
1568 "@#{admin.nickname} updated users: @#{user.nickname}"
1570 assert ModerationLog.get_log_entry_message(log_entry2) ==
1571 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
1574 test "returns 403 if requested by a non-admin", %{user: user} do
1577 |> assign(:user, user)
1578 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1579 "password" => "new_password",
1580 "email" => "new_email@example.com",
1581 "name" => "new_name"
1584 assert json_response(conn, :forbidden)
1587 test "changes actor type from permitted list", %{conn: conn, user: user} do
1588 assert user.actor_type == "Person"
1590 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1591 "actor_type" => "Service"
1593 |> json_response(200) == %{"status" => "success"}
1595 updated_user = User.get_by_id(user.id)
1597 assert updated_user.actor_type == "Service"
1599 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1600 "actor_type" => "Application"
1602 |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}
1605 test "update non existing user", %{conn: conn} do
1606 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
1607 "password" => "new_password"
1609 |> json_response(404) == %{"error" => "Not found"}
1613 describe "PATCH /users/:nickname/force_password_reset" do
1614 test "sets password_reset_pending to true", %{conn: conn} do
1615 user = insert(:user)
1616 assert user.password_reset_pending == false
1619 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
1621 assert json_response(conn, 204) == ""
1623 ObanHelpers.perform_all()
1625 assert User.get_by_id(user.id).password_reset_pending == true
1629 describe "instances" do
1630 test "GET /instances/:instance/statuses", %{conn: conn} do
1631 user = insert(:user, local: false, nickname: "archaeme@archae.me")
1632 user2 = insert(:user, local: false, nickname: "test@test.com")
1633 insert_pair(:note_activity, user: user)
1634 activity = insert(:note_activity, user: user2)
1636 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1638 response = json_response(ret_conn, 200)
1640 assert length(response) == 2
1642 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
1644 response = json_response(ret_conn, 200)
1646 assert length(response) == 1
1648 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
1650 response = json_response(ret_conn, 200)
1652 assert Enum.empty?(response)
1654 CommonAPI.repeat(activity.id, user)
1656 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1657 response = json_response(ret_conn, 200)
1658 assert length(response) == 2
1660 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
1661 response = json_response(ret_conn, 200)
1662 assert length(response) == 3
1666 describe "PATCH /confirm_email" do
1667 test "it confirms emails of two users", %{conn: conn, admin: admin} do
1668 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1670 assert first_user.confirmation_pending == true
1671 assert second_user.confirmation_pending == true
1674 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
1676 first_user.nickname,
1677 second_user.nickname
1681 assert ret_conn.status == 200
1683 assert first_user.confirmation_pending == true
1684 assert second_user.confirmation_pending == true
1686 log_entry = Repo.one(ModerationLog)
1688 assert ModerationLog.get_log_entry_message(log_entry) ==
1689 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
1690 second_user.nickname
1695 describe "PATCH /resend_confirmation_email" do
1696 test "it resend emails for two users", %{conn: conn, admin: admin} do
1697 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1700 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
1702 first_user.nickname,
1703 second_user.nickname
1707 assert ret_conn.status == 200
1709 log_entry = Repo.one(ModerationLog)
1711 assert ModerationLog.get_log_entry_message(log_entry) ==
1712 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
1713 second_user.nickname
1718 describe "/api/pleroma/admin/stats" do
1719 test "status visibility count", %{conn: conn} do
1720 admin = insert(:user, is_admin: true)
1721 user = insert(:user)
1722 CommonAPI.post(user, %{visibility: "public", status: "hey"})
1723 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1724 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1728 |> assign(:user, admin)
1729 |> get("/api/pleroma/admin/stats")
1730 |> json_response(200)
1732 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
1733 response["status_visibility"]
1736 test "by instance", %{conn: conn} do
1737 admin = insert(:user, is_admin: true)
1738 user1 = insert(:user)
1739 instance2 = "instance2.tld"
1740 user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
1742 CommonAPI.post(user1, %{visibility: "public", status: "hey"})
1743 CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"})
1744 CommonAPI.post(user2, %{visibility: "private", status: "hey"})
1748 |> assign(:user, admin)
1749 |> get("/api/pleroma/admin/stats", instance: instance2)
1750 |> json_response(200)
1752 assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
1753 response["status_visibility"]
1758 # Needed for testing
1759 defmodule Pleroma.Web.Endpoint.NotReal do
1762 defmodule Pleroma.Captcha.NotReal do