1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 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
13 alias Pleroma.ModerationLog
15 alias Pleroma.Tests.ObanHelpers
17 alias Pleroma.Web.ActivityPub.Relay
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.Endpoint
20 alias Pleroma.Web.MediaProxy
23 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
29 admin = insert(:user, is_admin: true)
30 token = insert(:oauth_admin_token, user: admin)
34 |> assign(:user, admin)
35 |> assign(:token, token)
37 {:ok, %{admin: admin, token: token, conn: conn}}
40 test "with valid `admin_token` query parameter, skips OAuth scopes check" do
41 clear_config([:admin_token], "password123")
46 get(build_conn(), "/api/v1/pleroma/admin/users/#{user.nickname}?admin_token=password123")
48 assert json_response_and_validate_schema(conn, 200)
51 test "GET /api/v1/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
54 url = "/api/v1/pleroma/admin/users/#{user.nickname}"
56 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
57 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
58 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
60 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
61 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
64 for good_token <- [good_token1, good_token2, good_token3] do
67 |> assign(:user, admin)
68 |> assign(:token, good_token)
71 assert json_response_and_validate_schema(conn, 200)
74 for good_token <- [good_token1, good_token2, good_token3] do
78 |> assign(:token, good_token)
81 assert json_response(conn, :forbidden)
84 for bad_token <- [bad_token1, bad_token2, bad_token3] do
87 |> assign(:user, admin)
88 |> assign(:token, bad_token)
91 assert json_response_and_validate_schema(conn, :forbidden)
95 describe "DELETE /api/v1/pleroma/admin/users" do
96 test "single user", %{admin: admin, conn: conn} do
97 clear_config([:instance, :federating], true)
101 avatar: %{"url" => [%{"href" => "https://someurl"}]},
102 banner: %{"url" => [%{"href" => "https://somebanner"}]},
107 # Create some activities to check they got deleted later
108 follower = insert(:user)
109 {:ok, _} = CommonAPI.post(user, %{status: "test"})
110 {:ok, _, _, _} = CommonAPI.follow(user, follower)
111 {:ok, _, _, _} = CommonAPI.follow(follower, user)
112 user = Repo.get(User, user.id)
113 assert user.note_count == 1
114 assert user.follower_count == 1
115 assert user.following_count == 1
116 assert user.is_active
118 with_mock Pleroma.Web.Federator,
119 publish: fn _ -> nil end,
120 perform: fn _, _ -> nil end do
123 |> put_req_header("accept", "application/json")
124 |> delete("/api/v1/pleroma/admin/users?nickname=#{user.nickname}")
126 ObanHelpers.perform_all()
128 refute User.get_by_nickname(user.nickname).is_active
130 log_entry = Repo.one(ModerationLog)
132 assert ModerationLog.get_log_entry_message(log_entry) ==
133 "@#{admin.nickname} deleted users: @#{user.nickname}"
135 assert json_response_and_validate_schema(conn, 200) == [user.nickname]
137 user = Repo.get(User, user.id)
138 refute user.is_active
140 assert user.avatar == %{}
141 assert user.banner == %{}
142 assert user.note_count == 0
143 assert user.follower_count == 0
144 assert user.following_count == 0
145 assert user.bio == ""
146 assert user.name == nil
148 assert called(Pleroma.Web.Federator.publish(:_))
152 test "multiple users", %{admin: admin, conn: conn} do
153 user_one = insert(:user)
154 user_two = insert(:user)
158 |> put_req_header("accept", "application/json")
159 |> put_req_header("content-type", "application/json")
160 |> delete("/api/v1/pleroma/admin/users", %{
161 nicknames: [user_one.nickname, user_two.nickname]
163 |> json_response_and_validate_schema(200)
165 log_entry = Repo.one(ModerationLog)
167 assert ModerationLog.get_log_entry_message(log_entry) ==
168 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
170 assert response -- [user_one.nickname, user_two.nickname] == []
174 describe "/api/v1/pleroma/admin/users" do
175 test "Create", %{conn: conn} do
178 |> put_req_header("accept", "application/json")
179 |> put_req_header("content-type", "application/json")
180 |> post("/api/v1/pleroma/admin/users", %{
183 "nickname" => "lain",
184 "email" => "lain@example.org",
188 "nickname" => "lain2",
189 "email" => "lain2@example.org",
194 |> json_response_and_validate_schema(200)
195 |> Enum.map(&Map.get(&1, "type"))
197 assert response == ["success", "success"]
199 log_entry = Repo.one(ModerationLog)
201 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
204 test "Cannot create user with existing email", %{conn: conn} do
209 |> put_req_header("accept", "application/json")
210 |> put_req_header("content-type", "application/json")
211 |> post("/api/v1/pleroma/admin/users", %{
214 "nickname" => "lain",
215 "email" => user.email,
221 assert json_response_and_validate_schema(conn, 409) == [
225 "email" => user.email,
228 "error" => "email has already been taken",
234 test "Cannot create user with existing nickname", %{conn: conn} do
239 |> put_req_header("accept", "application/json")
240 |> put_req_header("content-type", "application/json")
241 |> post("/api/v1/pleroma/admin/users", %{
244 "nickname" => user.nickname,
245 "email" => "someuser@plerama.social",
251 assert json_response_and_validate_schema(conn, 409) == [
255 "email" => "someuser@plerama.social",
256 "nickname" => user.nickname
258 "error" => "nickname has already been taken",
264 test "Multiple user creation works in transaction", %{conn: conn} do
269 |> put_req_header("accept", "application/json")
270 |> put_req_header("content-type", "application/json")
271 |> post("/api/v1/pleroma/admin/users", %{
274 "nickname" => "newuser",
275 "email" => "newuser@pleroma.social",
279 "nickname" => "lain",
280 "email" => user.email,
286 assert json_response_and_validate_schema(conn, 409) == [
290 "email" => user.email,
293 "error" => "email has already been taken",
299 "email" => "newuser@pleroma.social",
300 "nickname" => "newuser"
307 assert User.get_by_nickname("newuser") === nil
311 describe "/api/v1/pleroma/admin/users/:nickname" do
312 test "Show", %{conn: conn} do
315 conn = get(conn, "/api/v1/pleroma/admin/users/#{user.nickname}")
317 assert user_response(user) == json_response_and_validate_schema(conn, 200)
320 test "when the user doesn't exist", %{conn: conn} do
323 conn = get(conn, "/api/v1/pleroma/admin/users/#{user.nickname}")
325 assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404)
329 describe "/api/v1/pleroma/admin/users/follow" do
330 test "allows to force-follow another user", %{admin: admin, conn: conn} do
332 follower = insert(:user)
335 |> put_req_header("accept", "application/json")
336 |> put_req_header("content-type", "application/json")
337 |> post("/api/v1/pleroma/admin/users/follow", %{
338 "follower" => follower.nickname,
339 "followed" => user.nickname
342 user = User.get_cached_by_id(user.id)
343 follower = User.get_cached_by_id(follower.id)
345 assert User.following?(follower, user)
347 log_entry = Repo.one(ModerationLog)
349 assert ModerationLog.get_log_entry_message(log_entry) ==
350 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
354 describe "/api/v1/pleroma/admin/users/unfollow" do
355 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
357 follower = insert(:user)
359 User.follow(follower, user)
362 |> put_req_header("accept", "application/json")
363 |> put_req_header("content-type", "application/json")
364 |> post("/api/v1/pleroma/admin/users/unfollow", %{
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 refute 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} unfollow @#{user.nickname}"
381 describe "GET /api/v1/pleroma/admin/users" do
382 test "renders users array for the first page", %{conn: conn, admin: admin} do
383 user = insert(:user, local: false, tags: ["foo", "bar"])
384 user2 = insert(:user, is_approved: false, registration_reason: "I'm a chill dude")
386 conn = get(conn, "/api/v1/pleroma/admin/users?page=1")
393 "is_approved" => false,
394 "registration_reason" => "I'm a chill dude",
395 "actor_type" => "Person"
398 user_response(user, %{"local" => false, "tags" => ["foo", "bar"]}),
401 %{"roles" => %{"admin" => true, "moderator" => false}}
405 assert json_response_and_validate_schema(conn, 200) == %{
412 test "pagination works correctly with service users", %{conn: conn} do
413 service1 = User.get_or_create_service_actor_by_ap_id(Endpoint.url() <> "/meido", "meido")
415 insert_list(25, :user)
417 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
419 |> get("/api/v1/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
420 |> json_response_and_validate_schema(200)
422 assert Enum.count(users1) == 10
423 assert service1 not in users1
425 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
427 |> get("/api/v1/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
428 |> json_response_and_validate_schema(200)
430 assert Enum.count(users2) == 10
431 assert service1 not in users2
433 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
435 |> get("/api/v1/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
436 |> json_response_and_validate_schema(200)
438 assert Enum.count(users3) == 6
439 assert service1 not in users3
442 test "renders empty array for the second page", %{conn: conn} do
445 conn = get(conn, "/api/v1/pleroma/admin/users?page=2")
447 assert json_response_and_validate_schema(conn, 200) == %{
454 test "regular search", %{conn: conn} do
455 user = insert(:user, nickname: "bob")
457 conn = get(conn, "/api/v1/pleroma/admin/users?query=bo")
459 assert json_response_and_validate_schema(conn, 200) == %{
462 "users" => [user_response(user, %{"local" => true})]
466 test "search by domain", %{conn: conn} do
467 user = insert(:user, nickname: "nickname@domain.com")
470 conn = get(conn, "/api/v1/pleroma/admin/users?query=domain.com")
472 assert json_response_and_validate_schema(conn, 200) == %{
475 "users" => [user_response(user)]
479 test "search by full nickname", %{conn: conn} do
480 user = insert(:user, nickname: "nickname@domain.com")
483 conn = get(conn, "/api/v1/pleroma/admin/users?query=nickname@domain.com")
485 assert json_response_and_validate_schema(conn, 200) == %{
488 "users" => [user_response(user)]
492 test "search by display name", %{conn: conn} do
493 user = insert(:user, name: "Display name")
496 conn = get(conn, "/api/v1/pleroma/admin/users?name=display")
498 assert json_response_and_validate_schema(conn, 200) == %{
501 "users" => [user_response(user)]
505 test "search by email", %{conn: conn} do
506 user = insert(:user, email: "email@example.com")
509 conn = get(conn, "/api/v1/pleroma/admin/users?email=email@example.com")
511 assert json_response_and_validate_schema(conn, 200) == %{
514 "users" => [user_response(user)]
518 test "regular search with page size", %{conn: conn} do
519 user = insert(:user, nickname: "aalice")
520 user2 = insert(:user, nickname: "alice")
522 conn1 = get(conn, "/api/v1/pleroma/admin/users?query=a&page_size=1&page=1")
524 assert json_response_and_validate_schema(conn1, 200) == %{
527 "users" => [user_response(user2)]
530 conn2 = get(conn, "/api/v1/pleroma/admin/users?query=a&page_size=1&page=2")
532 assert json_response_and_validate_schema(conn2, 200) == %{
535 "users" => [user_response(user)]
539 test "only local users" do
540 admin = insert(:user, is_admin: true, nickname: "john")
541 token = insert(:oauth_admin_token, user: admin)
542 user = insert(:user, nickname: "bob")
544 insert(:user, nickname: "bobb", local: false)
548 |> assign(:user, admin)
549 |> assign(:token, token)
550 |> get("/api/v1/pleroma/admin/users?query=bo&filters=local")
552 assert json_response_and_validate_schema(conn, 200) == %{
555 "users" => [user_response(user)]
559 test "only local users with no query", %{conn: conn, admin: old_admin} do
560 admin = insert(:user, is_admin: true, nickname: "john")
561 user = insert(:user, nickname: "bob")
563 insert(:user, nickname: "bobb", local: false)
565 conn = get(conn, "/api/v1/pleroma/admin/users?filters=local")
569 user_response(admin, %{
570 "roles" => %{"admin" => true, "moderator" => false}
572 user_response(old_admin, %{
574 "roles" => %{"admin" => true, "moderator" => false}
578 assert json_response_and_validate_schema(conn, 200) == %{
585 test "only unconfirmed users", %{conn: conn} do
586 sad_user = insert(:user, nickname: "sadboy", is_confirmed: false)
587 old_user = insert(:user, nickname: "oldboy", is_confirmed: false)
589 insert(:user, nickname: "happyboy", is_approved: true)
590 insert(:user, is_confirmed: true)
594 |> get("/api/v1/pleroma/admin/users?filters=unconfirmed")
595 |> json_response_and_validate_schema(200)
598 Enum.map([old_user, sad_user], fn user ->
599 user_response(user, %{
600 "is_confirmed" => false,
601 "is_approved" => true
605 assert result == %{"count" => 2, "page_size" => 50, "users" => users}
608 test "only unapproved users", %{conn: conn} do
613 registration_reason: "Plz let me in!"
616 insert(:user, nickname: "happyboy", is_approved: true)
618 conn = get(conn, "/api/v1/pleroma/admin/users?filters=need_approval")
623 %{"is_approved" => false, "registration_reason" => "Plz let me in!"}
627 assert json_response_and_validate_schema(conn, 200) == %{
634 test "load only admins", %{conn: conn, admin: admin} do
635 second_admin = insert(:user, is_admin: true)
639 conn = get(conn, "/api/v1/pleroma/admin/users?filters=is_admin")
642 user_response(second_admin, %{
644 "roles" => %{"admin" => true, "moderator" => false}
646 user_response(admin, %{
648 "roles" => %{"admin" => true, "moderator" => false}
652 assert json_response_and_validate_schema(conn, 200) == %{
659 test "load only moderators", %{conn: conn} do
660 moderator = insert(:user, is_moderator: true)
664 conn = get(conn, "/api/v1/pleroma/admin/users?filters=is_moderator")
666 assert json_response_and_validate_schema(conn, 200) == %{
670 user_response(moderator, %{
672 "roles" => %{"admin" => false, "moderator" => true}
678 test "load users with actor_type is Person", %{admin: admin, conn: conn} do
679 insert(:user, actor_type: "Service")
680 insert(:user, actor_type: "Application")
682 user1 = insert(:user)
683 user2 = insert(:user)
687 |> get(user_path(conn, :index), %{actor_types: ["Person"]})
688 |> json_response_and_validate_schema(200)
691 user_response(user2),
692 user_response(user1),
693 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
696 assert response == %{"count" => 3, "page_size" => 50, "users" => users}
699 test "load users with actor_type is Person and Service", %{admin: admin, conn: conn} do
700 user_service = insert(:user, actor_type: "Service")
701 insert(:user, actor_type: "Application")
703 user1 = insert(:user)
704 user2 = insert(:user)
708 |> get(user_path(conn, :index), %{actor_types: ["Person", "Service"]})
709 |> json_response_and_validate_schema(200)
712 user_response(user2),
713 user_response(user1),
714 user_response(user_service, %{"actor_type" => "Service"}),
715 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
718 assert response == %{"count" => 4, "page_size" => 50, "users" => users}
721 test "load users with actor_type is Service", %{conn: conn} do
722 user_service = insert(:user, actor_type: "Service")
723 insert(:user, actor_type: "Application")
729 |> get(user_path(conn, :index), %{actor_types: ["Service"]})
730 |> json_response_and_validate_schema(200)
732 users = [user_response(user_service, %{"actor_type" => "Service"})]
734 assert response == %{"count" => 1, "page_size" => 50, "users" => users}
737 test "load users with tags list", %{conn: conn} do
738 user1 = insert(:user, tags: ["first"])
739 user2 = insert(:user, tags: ["second"])
743 conn = get(conn, "/api/v1/pleroma/admin/users?tags[]=first&tags[]=second")
746 user_response(user2, %{"tags" => ["second"]}),
747 user_response(user1, %{"tags" => ["first"]})
750 assert json_response_and_validate_schema(conn, 200) == %{
757 test "`active` filters out users pending approval", %{token: token} do
758 insert(:user, is_approved: false)
759 %{id: user_id} = insert(:user, is_approved: true)
760 %{id: admin_id} = token.user
764 |> assign(:user, token.user)
765 |> assign(:token, token)
766 |> get("/api/v1/pleroma/admin/users?filters=active")
775 } = json_response_and_validate_schema(conn, 200)
778 test "it works with multiple filters" do
779 admin = insert(:user, nickname: "john", is_admin: true)
780 token = insert(:oauth_admin_token, user: admin)
781 user = insert(:user, nickname: "bob", local: false, is_active: false)
783 insert(:user, nickname: "ken", local: true, is_active: false)
784 insert(:user, nickname: "bobb", local: false, is_active: true)
788 |> assign(:user, admin)
789 |> assign(:token, token)
790 |> get("/api/v1/pleroma/admin/users?filters=deactivated,external")
792 assert json_response_and_validate_schema(conn, 200) == %{
795 "users" => [user_response(user)]
799 test "it omits relay user", %{admin: admin, conn: conn} do
800 assert %User{} = Relay.get_actor()
802 conn = get(conn, "/api/v1/pleroma/admin/users")
804 assert json_response_and_validate_schema(conn, 200) == %{
808 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
814 test "PATCH /api/v1/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
815 user_one = insert(:user, is_active: false)
816 user_two = insert(:user, is_active: false)
820 |> put_req_header("content-type", "application/json")
822 "/api/v1/pleroma/admin/users/activate",
823 %{nicknames: [user_one.nickname, user_two.nickname]}
826 response = json_response_and_validate_schema(conn, 200)
827 assert Enum.map(response["users"], & &1["is_active"]) == [true, true]
829 log_entry = Repo.one(ModerationLog)
831 assert ModerationLog.get_log_entry_message(log_entry) ==
832 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
835 test "PATCH /api/v1/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
836 user_one = insert(:user, is_active: true)
837 user_two = insert(:user, is_active: true)
841 |> put_req_header("content-type", "application/json")
843 "/api/v1/pleroma/admin/users/deactivate",
844 %{nicknames: [user_one.nickname, user_two.nickname]}
847 response = json_response_and_validate_schema(conn, 200)
848 assert Enum.map(response["users"], & &1["is_active"]) == [false, false]
850 log_entry = Repo.one(ModerationLog)
852 assert ModerationLog.get_log_entry_message(log_entry) ==
853 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
856 test "PATCH /api/v1/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
857 user_one = insert(:user, is_approved: false)
858 user_two = insert(:user, is_approved: false)
862 |> put_req_header("content-type", "application/json")
864 "/api/v1/pleroma/admin/users/approve",
865 %{nicknames: [user_one.nickname, user_two.nickname]}
868 response = json_response_and_validate_schema(conn, 200)
869 assert Enum.map(response["users"], & &1["is_approved"]) == [true, true]
871 log_entry = Repo.one(ModerationLog)
873 assert ModerationLog.get_log_entry_message(log_entry) ==
874 "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
877 test "PATCH /api/v1/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
878 user1 = insert(:user, is_suggested: false)
879 user2 = insert(:user, is_suggested: false)
883 |> put_req_header("content-type", "application/json")
885 "/api/v1/pleroma/admin/users/suggest",
886 %{nicknames: [user1.nickname, user2.nickname]}
888 |> json_response_and_validate_schema(200)
890 assert Enum.map(response["users"], & &1["is_suggested"]) == [true, true]
891 [user1, user2] = Repo.reload!([user1, user2])
893 assert user1.is_suggested
894 assert user2.is_suggested
896 log_entry = Repo.one(ModerationLog)
898 assert ModerationLog.get_log_entry_message(log_entry) ==
899 "@#{admin.nickname} added suggested users: @#{user1.nickname}, @#{user2.nickname}"
902 test "PATCH /api/v1/pleroma/admin/users/unsuggest", %{admin: admin, conn: conn} do
903 user1 = insert(:user, is_suggested: true)
904 user2 = insert(:user, is_suggested: true)
908 |> put_req_header("content-type", "application/json")
910 "/api/v1/pleroma/admin/users/unsuggest",
911 %{nicknames: [user1.nickname, user2.nickname]}
913 |> json_response_and_validate_schema(200)
915 assert Enum.map(response["users"], & &1["is_suggested"]) == [false, false]
916 [user1, user2] = Repo.reload!([user1, user2])
918 refute user1.is_suggested
919 refute user2.is_suggested
921 log_entry = Repo.one(ModerationLog)
923 assert ModerationLog.get_log_entry_message(log_entry) ==
924 "@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
927 test "PATCH /api/v1/pleroma/admin/users/:nickname/toggle_activation", %{
935 |> put_req_header("content-type", "application/json")
936 |> patch("/api/v1/pleroma/admin/users/#{user.nickname}/toggle_activation")
938 assert json_response_and_validate_schema(conn, 200) ==
941 %{"is_active" => !user.is_active}
944 log_entry = Repo.one(ModerationLog)
946 assert ModerationLog.get_log_entry_message(log_entry) ==
947 "@#{admin.nickname} deactivated users: @#{user.nickname}"
950 defp user_response(user, attrs \\ %{}) do
952 "is_active" => user.is_active,
954 "email" => user.email,
955 "nickname" => user.nickname,
956 "roles" => %{"admin" => false, "moderator" => false},
957 "local" => user.local,
959 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
960 "display_name" => HTML.strip_tags(user.name || user.nickname),
961 "is_confirmed" => true,
962 "is_approved" => true,
963 "is_suggested" => false,
965 "registration_reason" => nil,
966 "actor_type" => "Person",
967 "created_at" => CommonAPI.Utils.to_masto_date(user.inserted_at)