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.UserControllerTest do
6 use Pleroma.Web.ConnCase
7 use Oban.Testing, repo: Pleroma.Repo
10 import Pleroma.Factory
14 alias Pleroma.ModerationLog
16 alias Pleroma.Tests.ObanHelpers
19 alias Pleroma.Web.ActivityPub.Relay
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Web.MediaProxy
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
30 admin = insert(:user, is_admin: true)
31 token = insert(:oauth_admin_token, user: admin)
35 |> assign(:user, admin)
36 |> assign(:token, token)
38 {:ok, %{admin: admin, token: token, conn: conn}}
41 test "with valid `admin_token` query parameter, skips OAuth scopes check" do
42 clear_config([:admin_token], "password123")
46 conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123")
48 assert json_response(conn, 200)
51 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
52 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
54 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
57 url = "/api/pleroma/admin/users/#{user.nickname}"
59 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
60 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
61 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
63 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
64 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
67 for good_token <- [good_token1, good_token2, good_token3] do
70 |> assign(:user, admin)
71 |> assign(:token, good_token)
74 assert json_response(conn, 200)
77 for good_token <- [good_token1, good_token2, good_token3] do
81 |> assign(:token, good_token)
84 assert json_response(conn, :forbidden)
87 for bad_token <- [bad_token1, bad_token2, bad_token3] do
90 |> assign(:user, admin)
91 |> assign(:token, bad_token)
94 assert json_response(conn, :forbidden)
99 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
100 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
102 test "GET /api/pleroma/admin/users/:nickname requires " <>
103 "read:accounts or admin:read:accounts or broader scope",
106 url = "/api/pleroma/admin/users/#{user.nickname}"
108 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
109 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
110 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
111 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
112 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
114 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
116 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
117 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
120 for good_token <- good_tokens do
123 |> assign(:user, admin)
124 |> assign(:token, good_token)
127 assert json_response(conn, 200)
130 for good_token <- good_tokens do
133 |> assign(:user, nil)
134 |> assign(:token, good_token)
137 assert json_response(conn, :forbidden)
140 for bad_token <- [bad_token1, bad_token2, bad_token3] do
143 |> assign(:user, admin)
144 |> assign(:token, bad_token)
147 assert json_response(conn, :forbidden)
152 describe "DELETE /api/pleroma/admin/users" do
153 test "single user", %{admin: admin, conn: conn} do
154 clear_config([:instance, :federating], true)
158 avatar: %{"url" => [%{"href" => "https://someurl"}]},
159 banner: %{"url" => [%{"href" => "https://somebanner"}]},
164 # Create some activities to check they got deleted later
165 follower = insert(:user)
166 {:ok, _} = CommonAPI.post(user, %{status: "test"})
167 {:ok, _, _, _} = CommonAPI.follow(user, follower)
168 {:ok, _, _, _} = CommonAPI.follow(follower, user)
169 user = Repo.get(User, user.id)
170 assert user.note_count == 1
171 assert user.follower_count == 1
172 assert user.following_count == 1
173 refute user.deactivated
175 with_mock Pleroma.Web.Federator,
176 publish: fn _ -> nil end,
177 perform: fn _, _ -> nil end do
180 |> put_req_header("accept", "application/json")
181 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
183 ObanHelpers.perform_all()
185 assert User.get_by_nickname(user.nickname).deactivated
187 log_entry = Repo.one(ModerationLog)
189 assert ModerationLog.get_log_entry_message(log_entry) ==
190 "@#{admin.nickname} deleted users: @#{user.nickname}"
192 assert json_response(conn, 200) == [user.nickname]
194 user = Repo.get(User, user.id)
195 assert user.deactivated
197 assert user.avatar == %{}
198 assert user.banner == %{}
199 assert user.note_count == 0
200 assert user.follower_count == 0
201 assert user.following_count == 0
202 assert user.bio == ""
203 assert user.name == nil
205 assert called(Pleroma.Web.Federator.publish(:_))
209 test "multiple users", %{admin: admin, conn: conn} do
210 user_one = insert(:user)
211 user_two = insert(:user)
215 |> put_req_header("accept", "application/json")
216 |> delete("/api/pleroma/admin/users", %{
217 nicknames: [user_one.nickname, user_two.nickname]
220 log_entry = Repo.one(ModerationLog)
222 assert ModerationLog.get_log_entry_message(log_entry) ==
223 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
225 response = json_response(conn, 200)
226 assert response -- [user_one.nickname, user_two.nickname] == []
230 describe "/api/pleroma/admin/users" do
231 test "Create", %{conn: conn} do
234 |> put_req_header("accept", "application/json")
235 |> post("/api/pleroma/admin/users", %{
238 "nickname" => "lain",
239 "email" => "lain@example.org",
243 "nickname" => "lain2",
244 "email" => "lain2@example.org",
250 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
251 assert response == ["success", "success"]
253 log_entry = Repo.one(ModerationLog)
255 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
258 test "Cannot create user with existing email", %{conn: conn} do
263 |> put_req_header("accept", "application/json")
264 |> post("/api/pleroma/admin/users", %{
267 "nickname" => "lain",
268 "email" => user.email,
274 assert json_response(conn, 409) == [
278 "email" => user.email,
281 "error" => "email has already been taken",
287 test "Cannot create user with existing nickname", %{conn: conn} do
292 |> put_req_header("accept", "application/json")
293 |> post("/api/pleroma/admin/users", %{
296 "nickname" => user.nickname,
297 "email" => "someuser@plerama.social",
303 assert json_response(conn, 409) == [
307 "email" => "someuser@plerama.social",
308 "nickname" => user.nickname
310 "error" => "nickname has already been taken",
316 test "Multiple user creation works in transaction", %{conn: conn} do
321 |> put_req_header("accept", "application/json")
322 |> post("/api/pleroma/admin/users", %{
325 "nickname" => "newuser",
326 "email" => "newuser@pleroma.social",
330 "nickname" => "lain",
331 "email" => user.email,
337 assert json_response(conn, 409) == [
341 "email" => user.email,
344 "error" => "email has already been taken",
350 "email" => "newuser@pleroma.social",
351 "nickname" => "newuser"
358 assert User.get_by_nickname("newuser") === nil
362 describe "/api/pleroma/admin/users/:nickname" do
363 test "Show", %{conn: conn} do
366 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
368 assert user_response(user) == json_response(conn, 200)
371 test "when the user doesn't exist", %{conn: conn} do
374 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
376 assert %{"error" => "Not found"} == json_response(conn, 404)
380 describe "/api/pleroma/admin/users/follow" do
381 test "allows to force-follow another user", %{admin: admin, conn: conn} do
383 follower = insert(:user)
386 |> put_req_header("accept", "application/json")
387 |> post("/api/pleroma/admin/users/follow", %{
388 "follower" => follower.nickname,
389 "followed" => user.nickname
392 user = User.get_cached_by_id(user.id)
393 follower = User.get_cached_by_id(follower.id)
395 assert User.following?(follower, user)
397 log_entry = Repo.one(ModerationLog)
399 assert ModerationLog.get_log_entry_message(log_entry) ==
400 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
404 describe "/api/pleroma/admin/users/unfollow" do
405 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
407 follower = insert(:user)
409 User.follow(follower, user)
412 |> put_req_header("accept", "application/json")
413 |> post("/api/pleroma/admin/users/unfollow", %{
414 "follower" => follower.nickname,
415 "followed" => user.nickname
418 user = User.get_cached_by_id(user.id)
419 follower = User.get_cached_by_id(follower.id)
421 refute User.following?(follower, user)
423 log_entry = Repo.one(ModerationLog)
425 assert ModerationLog.get_log_entry_message(log_entry) ==
426 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
430 describe "GET /api/pleroma/admin/users" do
431 test "renders users array for the first page", %{conn: conn, admin: admin} do
432 user = insert(:user, local: false, tags: ["foo", "bar"])
433 user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude")
435 conn = get(conn, "/api/pleroma/admin/users?page=1")
441 %{"roles" => %{"admin" => true, "moderator" => false}}
443 user_response(user, %{"local" => false, "tags" => ["foo", "bar"]}),
448 "approval_pending" => true,
449 "registration_reason" => "I'm a chill dude",
450 "actor_type" => "Person"
454 |> Enum.sort_by(& &1["nickname"])
456 assert json_response(conn, 200) == %{
463 test "pagination works correctly with service users", %{conn: conn} do
464 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
466 insert_list(25, :user)
468 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
470 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
471 |> json_response(200)
473 assert Enum.count(users1) == 10
474 assert service1 not in users1
476 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
478 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
479 |> json_response(200)
481 assert Enum.count(users2) == 10
482 assert service1 not in users2
484 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
486 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
487 |> json_response(200)
489 assert Enum.count(users3) == 6
490 assert service1 not in users3
493 test "renders empty array for the second page", %{conn: conn} do
496 conn = get(conn, "/api/pleroma/admin/users?page=2")
498 assert json_response(conn, 200) == %{
505 test "regular search", %{conn: conn} do
506 user = insert(:user, nickname: "bob")
508 conn = get(conn, "/api/pleroma/admin/users?query=bo")
510 assert json_response(conn, 200) == %{
513 "users" => [user_response(user, %{"local" => true})]
517 test "search by domain", %{conn: conn} do
518 user = insert(:user, nickname: "nickname@domain.com")
521 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
523 assert json_response(conn, 200) == %{
526 "users" => [user_response(user)]
530 test "search by full nickname", %{conn: conn} do
531 user = insert(:user, nickname: "nickname@domain.com")
534 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
536 assert json_response(conn, 200) == %{
539 "users" => [user_response(user)]
543 test "search by display name", %{conn: conn} do
544 user = insert(:user, name: "Display name")
547 conn = get(conn, "/api/pleroma/admin/users?name=display")
549 assert json_response(conn, 200) == %{
552 "users" => [user_response(user)]
556 test "search by email", %{conn: conn} do
557 user = insert(:user, email: "email@example.com")
560 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
562 assert json_response(conn, 200) == %{
565 "users" => [user_response(user)]
569 test "regular search with page size", %{conn: conn} do
570 user = insert(:user, nickname: "aalice")
571 user2 = insert(:user, nickname: "alice")
573 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
575 assert json_response(conn1, 200) == %{
578 "users" => [user_response(user)]
581 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
583 assert json_response(conn2, 200) == %{
586 "users" => [user_response(user2)]
590 test "only local users" do
591 admin = insert(:user, is_admin: true, nickname: "john")
592 token = insert(:oauth_admin_token, user: admin)
593 user = insert(:user, nickname: "bob")
595 insert(:user, nickname: "bobb", local: false)
599 |> assign(:user, admin)
600 |> assign(:token, token)
601 |> get("/api/pleroma/admin/users?query=bo&filters=local")
603 assert json_response(conn, 200) == %{
606 "users" => [user_response(user)]
610 test "only local users with no query", %{conn: conn, admin: old_admin} do
611 admin = insert(:user, is_admin: true, nickname: "john")
612 user = insert(:user, nickname: "bob")
614 insert(:user, nickname: "bobb", local: false)
616 conn = get(conn, "/api/pleroma/admin/users?filters=local")
621 user_response(admin, %{
622 "roles" => %{"admin" => true, "moderator" => false}
624 user_response(old_admin, %{
625 "deactivated" => false,
626 "roles" => %{"admin" => true, "moderator" => false}
629 |> Enum.sort_by(& &1["nickname"])
631 assert json_response(conn, 200) == %{
638 test "only unconfirmed users", %{conn: conn} do
639 sad_user = insert(:user, nickname: "sadboy", confirmation_pending: true)
640 old_user = insert(:user, nickname: "oldboy", confirmation_pending: true)
642 insert(:user, nickname: "happyboy", approval_pending: false)
643 insert(:user, confirmation_pending: false)
647 |> get("/api/pleroma/admin/users?filters=unconfirmed")
648 |> json_response(200)
651 Enum.map([old_user, sad_user], fn user ->
652 user_response(user, %{
653 "confirmation_pending" => true,
654 "approval_pending" => false
657 |> Enum.sort_by(& &1["nickname"])
659 assert result == %{"count" => 2, "page_size" => 50, "users" => users}
662 test "only unapproved users", %{conn: conn} do
666 approval_pending: true,
667 registration_reason: "Plz let me in!"
670 insert(:user, nickname: "happyboy", approval_pending: false)
672 conn = get(conn, "/api/pleroma/admin/users?filters=need_approval")
677 %{"approval_pending" => true, "registration_reason" => "Plz let me in!"}
681 assert json_response(conn, 200) == %{
688 test "load only admins", %{conn: conn, admin: admin} do
689 second_admin = insert(:user, is_admin: true)
693 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
697 user_response(admin, %{
698 "deactivated" => false,
699 "roles" => %{"admin" => true, "moderator" => false}
701 user_response(second_admin, %{
702 "deactivated" => false,
703 "roles" => %{"admin" => true, "moderator" => false}
706 |> Enum.sort_by(& &1["nickname"])
708 assert json_response(conn, 200) == %{
715 test "load only moderators", %{conn: conn} do
716 moderator = insert(:user, is_moderator: true)
720 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
722 assert json_response(conn, 200) == %{
726 user_response(moderator, %{
727 "deactivated" => false,
728 "roles" => %{"admin" => false, "moderator" => true}
734 test "load users with actor_type is Person", %{admin: admin, conn: conn} do
735 insert(:user, actor_type: "Service")
736 insert(:user, actor_type: "Application")
738 user1 = insert(:user)
739 user2 = insert(:user)
743 |> get(user_path(conn, :list), %{actor_types: ["Person"]})
744 |> json_response(200)
748 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
749 user_response(user1),
752 |> Enum.sort_by(& &1["nickname"])
754 assert response == %{"count" => 3, "page_size" => 50, "users" => users}
757 test "load users with actor_type is Person and Service", %{admin: admin, conn: conn} do
758 user_service = insert(:user, actor_type: "Service")
759 insert(:user, actor_type: "Application")
761 user1 = insert(:user)
762 user2 = insert(:user)
766 |> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]})
767 |> json_response(200)
771 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
772 user_response(user1),
773 user_response(user2),
774 user_response(user_service, %{"actor_type" => "Service"})
776 |> Enum.sort_by(& &1["nickname"])
778 assert response == %{"count" => 4, "page_size" => 50, "users" => users}
781 test "load users with actor_type is Service", %{conn: conn} do
782 user_service = insert(:user, actor_type: "Service")
783 insert(:user, actor_type: "Application")
789 |> get(user_path(conn, :list), %{actor_types: ["Service"]})
790 |> json_response(200)
792 users = [user_response(user_service, %{"actor_type" => "Service"})]
794 assert response == %{"count" => 1, "page_size" => 50, "users" => users}
797 test "load users with tags list", %{conn: conn} do
798 user1 = insert(:user, tags: ["first"])
799 user2 = insert(:user, tags: ["second"])
803 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
807 user_response(user1, %{"tags" => ["first"]}),
808 user_response(user2, %{"tags" => ["second"]})
810 |> Enum.sort_by(& &1["nickname"])
812 assert json_response(conn, 200) == %{
819 test "`active` filters out users pending approval", %{token: token} do
820 insert(:user, approval_pending: true)
821 %{id: user_id} = insert(:user, approval_pending: false)
822 %{id: admin_id} = token.user
826 |> assign(:user, token.user)
827 |> assign(:token, token)
828 |> get("/api/pleroma/admin/users?filters=active")
834 %{"id" => ^admin_id},
837 } = json_response(conn, 200)
840 test "it works with multiple filters" do
841 admin = insert(:user, nickname: "john", is_admin: true)
842 token = insert(:oauth_admin_token, user: admin)
843 user = insert(:user, nickname: "bob", local: false, deactivated: true)
845 insert(:user, nickname: "ken", local: true, deactivated: true)
846 insert(:user, nickname: "bobb", local: false, deactivated: false)
850 |> assign(:user, admin)
851 |> assign(:token, token)
852 |> get("/api/pleroma/admin/users?filters=deactivated,external")
854 assert json_response(conn, 200) == %{
857 "users" => [user_response(user)]
861 test "it omits relay user", %{admin: admin, conn: conn} do
862 assert %User{} = Relay.get_actor()
864 conn = get(conn, "/api/pleroma/admin/users")
866 assert json_response(conn, 200) == %{
870 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
876 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
877 user_one = insert(:user, deactivated: true)
878 user_two = insert(:user, deactivated: true)
883 "/api/pleroma/admin/users/activate",
884 %{nicknames: [user_one.nickname, user_two.nickname]}
887 response = json_response(conn, 200)
888 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
890 log_entry = Repo.one(ModerationLog)
892 assert ModerationLog.get_log_entry_message(log_entry) ==
893 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
896 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
897 user_one = insert(:user, deactivated: false)
898 user_two = insert(:user, deactivated: false)
903 "/api/pleroma/admin/users/deactivate",
904 %{nicknames: [user_one.nickname, user_two.nickname]}
907 response = json_response(conn, 200)
908 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
910 log_entry = Repo.one(ModerationLog)
912 assert ModerationLog.get_log_entry_message(log_entry) ==
913 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
916 test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
917 user_one = insert(:user, approval_pending: true)
918 user_two = insert(:user, approval_pending: true)
923 "/api/pleroma/admin/users/approve",
924 %{nicknames: [user_one.nickname, user_two.nickname]}
927 response = json_response(conn, 200)
928 assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false]
930 log_entry = Repo.one(ModerationLog)
932 assert ModerationLog.get_log_entry_message(log_entry) ==
933 "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
936 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
939 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
941 assert json_response(conn, 200) ==
944 %{"deactivated" => !user.deactivated}
947 log_entry = Repo.one(ModerationLog)
949 assert ModerationLog.get_log_entry_message(log_entry) ==
950 "@#{admin.nickname} deactivated users: @#{user.nickname}"
953 defp user_response(user, attrs \\ %{}) do
955 "deactivated" => user.deactivated,
957 "nickname" => user.nickname,
958 "roles" => %{"admin" => false, "moderator" => false},
959 "local" => user.local,
961 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
962 "display_name" => HTML.strip_tags(user.name || user.nickname),
963 "confirmation_pending" => false,
964 "approval_pending" => false,
966 "registration_reason" => nil,
967 "actor_type" => "Person"