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")
45 conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123")
47 assert json_response_and_validate_schema(conn, 200)
50 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
53 url = "/api/pleroma/admin/users/#{user.nickname}"
55 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
56 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
57 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
59 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
60 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
63 for good_token <- [good_token1, good_token2, good_token3] do
66 |> assign(:user, admin)
67 |> assign(:token, good_token)
70 assert json_response_and_validate_schema(conn, 200)
73 for good_token <- [good_token1, good_token2, good_token3] do
77 |> assign(:token, good_token)
80 assert json_response(conn, :forbidden)
83 for bad_token <- [bad_token1, bad_token2, bad_token3] do
86 |> assign(:user, admin)
87 |> assign(:token, bad_token)
90 assert json_response_and_validate_schema(conn, :forbidden)
94 describe "DELETE /api/pleroma/admin/users" do
95 test "single user", %{admin: admin, conn: conn} do
96 clear_config([:instance, :federating], true)
100 avatar: %{"url" => [%{"href" => "https://someurl"}]},
101 banner: %{"url" => [%{"href" => "https://somebanner"}]},
106 # Create some activities to check they got deleted later
107 follower = insert(:user)
108 {:ok, _} = CommonAPI.post(user, %{status: "test"})
109 {:ok, _, _, _} = CommonAPI.follow(user, follower)
110 {:ok, _, _, _} = CommonAPI.follow(follower, user)
111 user = Repo.get(User, user.id)
112 assert user.note_count == 1
113 assert user.follower_count == 1
114 assert user.following_count == 1
115 assert user.is_active
117 with_mock Pleroma.Web.Federator,
118 publish: fn _ -> nil end,
119 perform: fn _, _ -> nil end do
122 |> put_req_header("accept", "application/json")
123 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
125 ObanHelpers.perform_all()
127 refute User.get_by_nickname(user.nickname).is_active
129 log_entry = Repo.one(ModerationLog)
131 assert ModerationLog.get_log_entry_message(log_entry) ==
132 "@#{admin.nickname} deleted users: @#{user.nickname}"
134 assert json_response_and_validate_schema(conn, 200) == [user.nickname]
136 user = Repo.get(User, user.id)
137 refute user.is_active
139 assert user.avatar == %{}
140 assert user.banner == %{}
141 assert user.note_count == 0
142 assert user.follower_count == 0
143 assert user.following_count == 0
144 assert user.bio == ""
145 assert user.name == nil
147 assert called(Pleroma.Web.Federator.publish(:_))
151 test "multiple users", %{admin: admin, conn: conn} do
152 user_one = insert(:user)
153 user_two = insert(:user)
157 |> put_req_header("accept", "application/json")
158 |> put_req_header("content-type", "application/json")
159 |> delete("/api/pleroma/admin/users", %{
160 nicknames: [user_one.nickname, user_two.nickname]
162 |> json_response_and_validate_schema(200)
164 log_entry = Repo.one(ModerationLog)
166 assert ModerationLog.get_log_entry_message(log_entry) ==
167 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
169 assert response -- [user_one.nickname, user_two.nickname] == []
173 describe "/api/pleroma/admin/users" do
174 test "Create", %{conn: conn} do
177 |> put_req_header("accept", "application/json")
178 |> put_req_header("content-type", "application/json")
179 |> post("/api/pleroma/admin/users", %{
182 "nickname" => "lain",
183 "email" => "lain@example.org",
187 "nickname" => "lain2",
188 "email" => "lain2@example.org",
193 |> json_response_and_validate_schema(200)
194 |> Enum.map(&Map.get(&1, "type"))
196 assert response == ["success", "success"]
198 log_entry = Repo.one(ModerationLog)
200 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
203 test "Cannot create user with existing email", %{conn: conn} do
208 |> put_req_header("accept", "application/json")
209 |> put_req_header("content-type", "application/json")
210 |> post("/api/pleroma/admin/users", %{
213 "nickname" => "lain",
214 "email" => user.email,
220 assert json_response_and_validate_schema(conn, 409) == [
224 "email" => user.email,
227 "error" => "email has already been taken",
233 test "Cannot create user with existing nickname", %{conn: conn} do
238 |> put_req_header("accept", "application/json")
239 |> put_req_header("content-type", "application/json")
240 |> post("/api/pleroma/admin/users", %{
243 "nickname" => user.nickname,
244 "email" => "someuser@plerama.social",
250 assert json_response_and_validate_schema(conn, 409) == [
254 "email" => "someuser@plerama.social",
255 "nickname" => user.nickname
257 "error" => "nickname has already been taken",
263 test "Multiple user creation works in transaction", %{conn: conn} do
268 |> put_req_header("accept", "application/json")
269 |> put_req_header("content-type", "application/json")
270 |> post("/api/pleroma/admin/users", %{
273 "nickname" => "newuser",
274 "email" => "newuser@pleroma.social",
278 "nickname" => "lain",
279 "email" => user.email,
285 assert json_response_and_validate_schema(conn, 409) == [
289 "email" => user.email,
292 "error" => "email has already been taken",
298 "email" => "newuser@pleroma.social",
299 "nickname" => "newuser"
306 assert User.get_by_nickname("newuser") === nil
310 describe "/api/pleroma/admin/users/:nickname" do
311 test "Show", %{conn: conn} do
314 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
316 assert user_response(user) == json_response_and_validate_schema(conn, 200)
319 test "when the user doesn't exist", %{conn: conn} do
322 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
324 assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404)
328 describe "/api/pleroma/admin/users/follow" do
329 test "allows to force-follow another user", %{admin: admin, conn: conn} do
331 follower = insert(:user)
334 |> put_req_header("accept", "application/json")
335 |> put_req_header("content-type", "application/json")
336 |> post("/api/pleroma/admin/users/follow", %{
337 "follower" => follower.nickname,
338 "followed" => user.nickname
341 user = User.get_cached_by_id(user.id)
342 follower = User.get_cached_by_id(follower.id)
344 assert User.following?(follower, user)
346 log_entry = Repo.one(ModerationLog)
348 assert ModerationLog.get_log_entry_message(log_entry) ==
349 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
353 describe "/api/pleroma/admin/users/unfollow" do
354 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
356 follower = insert(:user)
358 User.follow(follower, user)
361 |> put_req_header("accept", "application/json")
362 |> put_req_header("content-type", "application/json")
363 |> post("/api/pleroma/admin/users/unfollow", %{
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 refute 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} unfollow @#{user.nickname}"
380 describe "GET /api/pleroma/admin/users" do
381 test "renders users array for the first page", %{conn: conn, admin: admin} do
382 user = insert(:user, local: false, tags: ["foo", "bar"])
383 user2 = insert(:user, is_approved: false, registration_reason: "I'm a chill dude")
385 conn = get(conn, "/api/pleroma/admin/users?page=1")
391 %{"roles" => %{"admin" => true, "moderator" => false}}
393 user_response(user, %{"local" => false, "tags" => ["foo", "bar"]}),
398 "is_approved" => false,
399 "registration_reason" => "I'm a chill dude",
400 "actor_type" => "Person"
404 |> Enum.sort_by(& &1["nickname"])
406 assert json_response_and_validate_schema(conn, 200) == %{
413 test "pagination works correctly with service users", %{conn: conn} do
414 service1 = User.get_or_create_service_actor_by_ap_id(Endpoint.url() <> "/meido", "meido")
416 insert_list(25, :user)
418 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
420 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
421 |> json_response_and_validate_schema(200)
423 assert Enum.count(users1) == 10
424 assert service1 not in users1
426 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
428 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
429 |> json_response_and_validate_schema(200)
431 assert Enum.count(users2) == 10
432 assert service1 not in users2
434 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
436 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
437 |> json_response_and_validate_schema(200)
439 assert Enum.count(users3) == 6
440 assert service1 not in users3
443 test "renders empty array for the second page", %{conn: conn} do
446 conn = get(conn, "/api/pleroma/admin/users?page=2")
448 assert json_response_and_validate_schema(conn, 200) == %{
455 test "regular search", %{conn: conn} do
456 user = insert(:user, nickname: "bob")
458 conn = get(conn, "/api/pleroma/admin/users?query=bo")
460 assert json_response_and_validate_schema(conn, 200) == %{
463 "users" => [user_response(user, %{"local" => true})]
467 test "search by domain", %{conn: conn} do
468 user = insert(:user, nickname: "nickname@domain.com")
471 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
473 assert json_response_and_validate_schema(conn, 200) == %{
476 "users" => [user_response(user)]
480 test "search by full nickname", %{conn: conn} do
481 user = insert(:user, nickname: "nickname@domain.com")
484 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
486 assert json_response_and_validate_schema(conn, 200) == %{
489 "users" => [user_response(user)]
493 test "search by display name", %{conn: conn} do
494 user = insert(:user, name: "Display name")
497 conn = get(conn, "/api/pleroma/admin/users?name=display")
499 assert json_response_and_validate_schema(conn, 200) == %{
502 "users" => [user_response(user)]
506 test "search by email", %{conn: conn} do
507 user = insert(:user, email: "email@example.com")
510 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
512 assert json_response_and_validate_schema(conn, 200) == %{
515 "users" => [user_response(user)]
519 test "regular search with page size", %{conn: conn} do
520 user = insert(:user, nickname: "aalice")
521 user2 = insert(:user, nickname: "alice")
523 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
525 assert json_response_and_validate_schema(conn1, 200) == %{
528 "users" => [user_response(user)]
531 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
533 assert json_response_and_validate_schema(conn2, 200) == %{
536 "users" => [user_response(user2)]
540 test "only local users" do
541 admin = insert(:user, is_admin: true, nickname: "john")
542 token = insert(:oauth_admin_token, user: admin)
543 user = insert(:user, nickname: "bob")
545 insert(:user, nickname: "bobb", local: false)
549 |> assign(:user, admin)
550 |> assign(:token, token)
551 |> get("/api/pleroma/admin/users?query=bo&filters=local")
553 assert json_response_and_validate_schema(conn, 200) == %{
556 "users" => [user_response(user)]
560 test "only local users with no query", %{conn: conn, admin: old_admin} do
561 admin = insert(:user, is_admin: true, nickname: "john")
562 user = insert(:user, nickname: "bob")
564 insert(:user, nickname: "bobb", local: false)
566 conn = get(conn, "/api/pleroma/admin/users?filters=local")
571 user_response(admin, %{
572 "roles" => %{"admin" => true, "moderator" => false}
574 user_response(old_admin, %{
576 "roles" => %{"admin" => true, "moderator" => false}
579 |> Enum.sort_by(& &1["nickname"])
581 assert json_response_and_validate_schema(conn, 200) == %{
588 test "only unconfirmed users", %{conn: conn} do
589 sad_user = insert(:user, nickname: "sadboy", is_confirmed: false)
590 old_user = insert(:user, nickname: "oldboy", is_confirmed: false)
592 insert(:user, nickname: "happyboy", is_approved: true)
593 insert(:user, is_confirmed: true)
597 |> get("/api/pleroma/admin/users?filters=unconfirmed")
598 |> json_response_and_validate_schema(200)
601 Enum.map([old_user, sad_user], fn user ->
602 user_response(user, %{
603 "is_confirmed" => false,
604 "is_approved" => true
607 |> Enum.sort_by(& &1["nickname"])
609 assert result == %{"count" => 2, "page_size" => 50, "users" => users}
612 test "only unapproved users", %{conn: conn} do
617 registration_reason: "Plz let me in!"
620 insert(:user, nickname: "happyboy", is_approved: true)
622 conn = get(conn, "/api/pleroma/admin/users?filters=need_approval")
627 %{"is_approved" => false, "registration_reason" => "Plz let me in!"}
631 assert json_response_and_validate_schema(conn, 200) == %{
638 test "load only admins", %{conn: conn, admin: admin} do
639 second_admin = insert(:user, is_admin: true)
643 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
647 user_response(admin, %{
649 "roles" => %{"admin" => true, "moderator" => false}
651 user_response(second_admin, %{
653 "roles" => %{"admin" => true, "moderator" => false}
656 |> Enum.sort_by(& &1["nickname"])
658 assert json_response_and_validate_schema(conn, 200) == %{
665 test "load only moderators", %{conn: conn} do
666 moderator = insert(:user, is_moderator: true)
670 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
672 assert json_response_and_validate_schema(conn, 200) == %{
676 user_response(moderator, %{
678 "roles" => %{"admin" => false, "moderator" => true}
684 test "load users with actor_type is Person", %{admin: admin, conn: conn} do
685 insert(:user, actor_type: "Service")
686 insert(:user, actor_type: "Application")
688 user1 = insert(:user)
689 user2 = insert(:user)
693 |> get(user_path(conn, :index), %{actor_types: ["Person"]})
694 |> json_response_and_validate_schema(200)
698 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
699 user_response(user1),
702 |> Enum.sort_by(& &1["nickname"])
704 assert response == %{"count" => 3, "page_size" => 50, "users" => users}
707 test "load users with actor_type is Person and Service", %{admin: admin, conn: conn} do
708 user_service = insert(:user, actor_type: "Service")
709 insert(:user, actor_type: "Application")
711 user1 = insert(:user)
712 user2 = insert(:user)
716 |> get(user_path(conn, :index), %{actor_types: ["Person", "Service"]})
717 |> json_response_and_validate_schema(200)
721 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
722 user_response(user1),
723 user_response(user2),
724 user_response(user_service, %{"actor_type" => "Service"})
726 |> Enum.sort_by(& &1["nickname"])
728 assert response == %{"count" => 4, "page_size" => 50, "users" => users}
731 test "load users with actor_type is Service", %{conn: conn} do
732 user_service = insert(:user, actor_type: "Service")
733 insert(:user, actor_type: "Application")
739 |> get(user_path(conn, :index), %{actor_types: ["Service"]})
740 |> json_response_and_validate_schema(200)
742 users = [user_response(user_service, %{"actor_type" => "Service"})]
744 assert response == %{"count" => 1, "page_size" => 50, "users" => users}
747 test "load users with tags list", %{conn: conn} do
748 user1 = insert(:user, tags: ["first"])
749 user2 = insert(:user, tags: ["second"])
753 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
757 user_response(user1, %{"tags" => ["first"]}),
758 user_response(user2, %{"tags" => ["second"]})
760 |> Enum.sort_by(& &1["nickname"])
762 assert json_response_and_validate_schema(conn, 200) == %{
769 test "`active` filters out users pending approval", %{token: token} do
770 insert(:user, is_approved: false)
771 %{id: user_id} = insert(:user, is_approved: true)
772 %{id: admin_id} = token.user
776 |> assign(:user, token.user)
777 |> assign(:token, token)
778 |> get("/api/pleroma/admin/users?filters=active")
784 %{"id" => ^admin_id},
787 } = json_response_and_validate_schema(conn, 200)
790 test "it works with multiple filters" do
791 admin = insert(:user, nickname: "john", is_admin: true)
792 token = insert(:oauth_admin_token, user: admin)
793 user = insert(:user, nickname: "bob", local: false, is_active: false)
795 insert(:user, nickname: "ken", local: true, is_active: false)
796 insert(:user, nickname: "bobb", local: false, is_active: true)
800 |> assign(:user, admin)
801 |> assign(:token, token)
802 |> get("/api/pleroma/admin/users?filters=deactivated,external")
804 assert json_response_and_validate_schema(conn, 200) == %{
807 "users" => [user_response(user)]
811 test "it omits relay user", %{admin: admin, conn: conn} do
812 assert %User{} = Relay.get_actor()
814 conn = get(conn, "/api/pleroma/admin/users")
816 assert json_response_and_validate_schema(conn, 200) == %{
820 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
826 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
827 user_one = insert(:user, is_active: false)
828 user_two = insert(:user, is_active: false)
832 |> put_req_header("content-type", "application/json")
834 "/api/pleroma/admin/users/activate",
835 %{nicknames: [user_one.nickname, user_two.nickname]}
838 response = json_response_and_validate_schema(conn, 200)
839 assert Enum.map(response["users"], & &1["is_active"]) == [true, true]
841 log_entry = Repo.one(ModerationLog)
843 assert ModerationLog.get_log_entry_message(log_entry) ==
844 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
847 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
848 user_one = insert(:user, is_active: true)
849 user_two = insert(:user, is_active: true)
853 |> put_req_header("content-type", "application/json")
855 "/api/pleroma/admin/users/deactivate",
856 %{nicknames: [user_one.nickname, user_two.nickname]}
859 response = json_response_and_validate_schema(conn, 200)
860 assert Enum.map(response["users"], & &1["is_active"]) == [false, false]
862 log_entry = Repo.one(ModerationLog)
864 assert ModerationLog.get_log_entry_message(log_entry) ==
865 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
868 test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
869 user_one = insert(:user, is_approved: false)
870 user_two = insert(:user, is_approved: false)
874 |> put_req_header("content-type", "application/json")
876 "/api/pleroma/admin/users/approve",
877 %{nicknames: [user_one.nickname, user_two.nickname]}
880 response = json_response_and_validate_schema(conn, 200)
881 assert Enum.map(response["users"], & &1["is_approved"]) == [true, true]
883 log_entry = Repo.one(ModerationLog)
885 assert ModerationLog.get_log_entry_message(log_entry) ==
886 "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
889 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
894 |> put_req_header("content-type", "application/json")
895 |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
897 assert json_response_and_validate_schema(conn, 200) ==
900 %{"is_active" => !user.is_active}
903 log_entry = Repo.one(ModerationLog)
905 assert ModerationLog.get_log_entry_message(log_entry) ==
906 "@#{admin.nickname} deactivated users: @#{user.nickname}"
909 defp user_response(user, attrs \\ %{}) do
911 "is_active" => user.is_active,
913 "email" => user.email,
914 "nickname" => user.nickname,
915 "roles" => %{"admin" => false, "moderator" => false},
916 "local" => user.local,
918 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
919 "display_name" => HTML.strip_tags(user.name || user.nickname),
920 "is_confirmed" => true,
921 "is_approved" => true,
923 "registration_reason" => nil,
924 "actor_type" => "Person"