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
12 import Swoosh.TestAssertions
14 alias Pleroma.Activity
18 alias Pleroma.ModerationLog
20 alias Pleroma.Tests.ObanHelpers
23 alias Pleroma.Web.ActivityPub.Relay
24 alias Pleroma.Web.CommonAPI
25 alias Pleroma.Web.MediaProxy
28 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
34 admin = insert(:user, is_admin: true)
35 token = insert(:oauth_admin_token, user: admin)
39 |> assign(:user, admin)
40 |> assign(:token, token)
42 {:ok, %{admin: admin, token: token, conn: conn}}
45 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
46 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
48 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
51 url = "/api/pleroma/admin/users/#{user.nickname}"
53 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
54 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
55 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
57 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
58 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
61 for good_token <- [good_token1, good_token2, good_token3] do
64 |> assign(:user, admin)
65 |> assign(:token, good_token)
68 assert json_response(conn, 200)
71 for good_token <- [good_token1, good_token2, good_token3] do
75 |> assign(:token, good_token)
78 assert json_response(conn, :forbidden)
81 for bad_token <- [bad_token1, bad_token2, bad_token3] do
84 |> assign(:user, admin)
85 |> assign(:token, bad_token)
88 assert json_response(conn, :forbidden)
93 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
94 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
96 test "GET /api/pleroma/admin/users/:nickname requires " <>
97 "read:accounts or admin:read:accounts or broader scope",
100 url = "/api/pleroma/admin/users/#{user.nickname}"
102 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
103 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
104 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
105 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
106 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
108 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
110 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
111 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
114 for good_token <- good_tokens do
117 |> assign(:user, admin)
118 |> assign(:token, good_token)
121 assert json_response(conn, 200)
124 for good_token <- good_tokens do
127 |> assign(:user, nil)
128 |> assign(:token, good_token)
131 assert json_response(conn, :forbidden)
134 for bad_token <- [bad_token1, bad_token2, bad_token3] do
137 |> assign(:user, admin)
138 |> assign(:token, bad_token)
141 assert json_response(conn, :forbidden)
146 describe "DELETE /api/pleroma/admin/users" do
147 test "single user", %{admin: admin, conn: conn} do
149 clear_config([:instance, :federating], true)
151 with_mock Pleroma.Web.Federator,
152 publish: fn _ -> nil end do
155 |> put_req_header("accept", "application/json")
156 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
158 ObanHelpers.perform_all()
160 assert User.get_by_nickname(user.nickname).deactivated
162 log_entry = Repo.one(ModerationLog)
164 assert ModerationLog.get_log_entry_message(log_entry) ==
165 "@#{admin.nickname} deleted users: @#{user.nickname}"
167 assert json_response(conn, 200) == [user.nickname]
169 assert called(Pleroma.Web.Federator.publish(:_))
173 test "multiple users", %{admin: admin, conn: conn} do
174 user_one = insert(:user)
175 user_two = insert(:user)
179 |> put_req_header("accept", "application/json")
180 |> delete("/api/pleroma/admin/users", %{
181 nicknames: [user_one.nickname, user_two.nickname]
184 log_entry = Repo.one(ModerationLog)
186 assert ModerationLog.get_log_entry_message(log_entry) ==
187 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
189 response = json_response(conn, 200)
190 assert response -- [user_one.nickname, user_two.nickname] == []
194 describe "/api/pleroma/admin/users" do
195 test "Create", %{conn: conn} do
198 |> put_req_header("accept", "application/json")
199 |> post("/api/pleroma/admin/users", %{
202 "nickname" => "lain",
203 "email" => "lain@example.org",
207 "nickname" => "lain2",
208 "email" => "lain2@example.org",
214 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
215 assert response == ["success", "success"]
217 log_entry = Repo.one(ModerationLog)
219 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
222 test "Cannot create user with existing email", %{conn: conn} do
227 |> put_req_header("accept", "application/json")
228 |> post("/api/pleroma/admin/users", %{
231 "nickname" => "lain",
232 "email" => user.email,
238 assert json_response(conn, 409) == [
242 "email" => user.email,
245 "error" => "email has already been taken",
251 test "Cannot create user with existing nickname", %{conn: conn} do
256 |> put_req_header("accept", "application/json")
257 |> post("/api/pleroma/admin/users", %{
260 "nickname" => user.nickname,
261 "email" => "someuser@plerama.social",
267 assert json_response(conn, 409) == [
271 "email" => "someuser@plerama.social",
272 "nickname" => user.nickname
274 "error" => "nickname has already been taken",
280 test "Multiple user creation works in transaction", %{conn: conn} do
285 |> put_req_header("accept", "application/json")
286 |> post("/api/pleroma/admin/users", %{
289 "nickname" => "newuser",
290 "email" => "newuser@pleroma.social",
294 "nickname" => "lain",
295 "email" => user.email,
301 assert json_response(conn, 409) == [
305 "email" => user.email,
308 "error" => "email has already been taken",
314 "email" => "newuser@pleroma.social",
315 "nickname" => "newuser"
322 assert User.get_by_nickname("newuser") === nil
326 describe "/api/pleroma/admin/users/:nickname" do
327 test "Show", %{conn: conn} do
330 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
333 "deactivated" => false,
334 "id" => to_string(user.id),
336 "nickname" => user.nickname,
337 "roles" => %{"admin" => false, "moderator" => false},
339 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
340 "display_name" => HTML.strip_tags(user.name || user.nickname),
341 "confirmation_pending" => false,
345 assert expected == json_response(conn, 200)
348 test "when the user doesn't exist", %{conn: conn} do
351 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
353 assert %{"error" => "Not found"} == json_response(conn, 404)
357 describe "/api/pleroma/admin/users/follow" do
358 test "allows to force-follow another user", %{admin: admin, conn: conn} do
360 follower = insert(:user)
363 |> put_req_header("accept", "application/json")
364 |> post("/api/pleroma/admin/users/follow", %{
365 "follower" => follower.nickname,
366 "followed" => user.nickname
369 user = User.get_cached_by_id(user.id)
370 follower = User.get_cached_by_id(follower.id)
372 assert User.following?(follower, user)
374 log_entry = Repo.one(ModerationLog)
376 assert ModerationLog.get_log_entry_message(log_entry) ==
377 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
381 describe "/api/pleroma/admin/users/unfollow" do
382 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
384 follower = insert(:user)
386 User.follow(follower, user)
389 |> put_req_header("accept", "application/json")
390 |> post("/api/pleroma/admin/users/unfollow", %{
391 "follower" => follower.nickname,
392 "followed" => user.nickname
395 user = User.get_cached_by_id(user.id)
396 follower = User.get_cached_by_id(follower.id)
398 refute User.following?(follower, user)
400 log_entry = Repo.one(ModerationLog)
402 assert ModerationLog.get_log_entry_message(log_entry) ==
403 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
407 describe "PUT /api/pleroma/admin/users/tag" do
408 setup %{conn: conn} do
409 user1 = insert(:user, %{tags: ["x"]})
410 user2 = insert(:user, %{tags: ["y"]})
411 user3 = insert(:user, %{tags: ["unchanged"]})
415 |> put_req_header("accept", "application/json")
417 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
418 "#{user2.nickname}&tags[]=foo&tags[]=bar"
421 %{conn: conn, user1: user1, user2: user2, user3: user3}
424 test "it appends specified tags to users with specified nicknames", %{
430 assert json_response(conn, :no_content)
431 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
432 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
434 log_entry = Repo.one(ModerationLog)
437 [user1.nickname, user2.nickname]
438 |> Enum.map(&"@#{&1}")
441 tags = ["foo", "bar"] |> Enum.join(", ")
443 assert ModerationLog.get_log_entry_message(log_entry) ==
444 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
447 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
448 assert json_response(conn, :no_content)
449 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
453 describe "DELETE /api/pleroma/admin/users/tag" do
454 setup %{conn: conn} do
455 user1 = insert(:user, %{tags: ["x"]})
456 user2 = insert(:user, %{tags: ["y", "z"]})
457 user3 = insert(:user, %{tags: ["unchanged"]})
461 |> put_req_header("accept", "application/json")
463 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
464 "#{user2.nickname}&tags[]=x&tags[]=z"
467 %{conn: conn, user1: user1, user2: user2, user3: user3}
470 test "it removes specified tags from users with specified nicknames", %{
476 assert json_response(conn, :no_content)
477 assert User.get_cached_by_id(user1.id).tags == []
478 assert User.get_cached_by_id(user2.id).tags == ["y"]
480 log_entry = Repo.one(ModerationLog)
483 [user1.nickname, user2.nickname]
484 |> Enum.map(&"@#{&1}")
487 tags = ["x", "z"] |> Enum.join(", ")
489 assert ModerationLog.get_log_entry_message(log_entry) ==
490 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
493 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
494 assert json_response(conn, :no_content)
495 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
499 describe "/api/pleroma/admin/users/:nickname/permission_group" do
500 test "GET is giving user_info", %{admin: admin, conn: conn} do
503 |> put_req_header("accept", "application/json")
504 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
506 assert json_response(conn, 200) == %{
508 "is_moderator" => false
512 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
517 |> put_req_header("accept", "application/json")
518 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
520 assert json_response(conn, 200) == %{
524 log_entry = Repo.one(ModerationLog)
526 assert ModerationLog.get_log_entry_message(log_entry) ==
527 "@#{admin.nickname} made @#{user.nickname} admin"
530 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
531 user_one = insert(:user)
532 user_two = insert(:user)
536 |> put_req_header("accept", "application/json")
537 |> post("/api/pleroma/admin/users/permission_group/admin", %{
538 nicknames: [user_one.nickname, user_two.nickname]
541 assert json_response(conn, 200) == %{"is_admin" => true}
543 log_entry = Repo.one(ModerationLog)
545 assert ModerationLog.get_log_entry_message(log_entry) ==
546 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
549 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
550 user = insert(:user, is_admin: true)
554 |> put_req_header("accept", "application/json")
555 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
557 assert json_response(conn, 200) == %{"is_admin" => false}
559 log_entry = Repo.one(ModerationLog)
561 assert ModerationLog.get_log_entry_message(log_entry) ==
562 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
565 test "/:right DELETE, can remove from a permission group (multiple)", %{
569 user_one = insert(:user, is_admin: true)
570 user_two = insert(:user, is_admin: true)
574 |> put_req_header("accept", "application/json")
575 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
576 nicknames: [user_one.nickname, user_two.nickname]
579 assert json_response(conn, 200) == %{"is_admin" => false}
581 log_entry = Repo.one(ModerationLog)
583 assert ModerationLog.get_log_entry_message(log_entry) ==
584 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
590 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
595 |> put_req_header("accept", "application/json")
596 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
598 resp = json_response(conn, 200)
600 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
603 describe "GET /api/pleroma/admin/users" do
604 test "renders users array for the first page", %{conn: conn, admin: admin} do
605 user = insert(:user, local: false, tags: ["foo", "bar"])
606 conn = get(conn, "/api/pleroma/admin/users?page=1")
611 "deactivated" => admin.deactivated,
613 "nickname" => admin.nickname,
614 "roles" => %{"admin" => true, "moderator" => false},
617 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
618 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
619 "confirmation_pending" => false,
623 "deactivated" => user.deactivated,
625 "nickname" => user.nickname,
626 "roles" => %{"admin" => false, "moderator" => false},
628 "tags" => ["foo", "bar"],
629 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
630 "display_name" => HTML.strip_tags(user.name || user.nickname),
631 "confirmation_pending" => false,
635 |> Enum.sort_by(& &1["nickname"])
637 assert json_response(conn, 200) == %{
644 test "pagination works correctly with service users", %{conn: conn} do
645 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
647 insert_list(25, :user)
649 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
651 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
652 |> json_response(200)
654 assert Enum.count(users1) == 10
655 assert service1 not in users1
657 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
659 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
660 |> json_response(200)
662 assert Enum.count(users2) == 10
663 assert service1 not in users2
665 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
667 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
668 |> json_response(200)
670 assert Enum.count(users3) == 6
671 assert service1 not in users3
674 test "renders empty array for the second page", %{conn: conn} do
677 conn = get(conn, "/api/pleroma/admin/users?page=2")
679 assert json_response(conn, 200) == %{
686 test "regular search", %{conn: conn} do
687 user = insert(:user, nickname: "bob")
689 conn = get(conn, "/api/pleroma/admin/users?query=bo")
691 assert json_response(conn, 200) == %{
696 "deactivated" => user.deactivated,
698 "nickname" => user.nickname,
699 "roles" => %{"admin" => false, "moderator" => false},
702 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
703 "display_name" => HTML.strip_tags(user.name || user.nickname),
704 "confirmation_pending" => false,
711 test "search by domain", %{conn: conn} do
712 user = insert(:user, nickname: "nickname@domain.com")
715 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
717 assert json_response(conn, 200) == %{
722 "deactivated" => user.deactivated,
724 "nickname" => user.nickname,
725 "roles" => %{"admin" => false, "moderator" => false},
728 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
729 "display_name" => HTML.strip_tags(user.name || user.nickname),
730 "confirmation_pending" => false,
737 test "search by full nickname", %{conn: conn} do
738 user = insert(:user, nickname: "nickname@domain.com")
741 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
743 assert json_response(conn, 200) == %{
748 "deactivated" => user.deactivated,
750 "nickname" => user.nickname,
751 "roles" => %{"admin" => false, "moderator" => false},
754 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
755 "display_name" => HTML.strip_tags(user.name || user.nickname),
756 "confirmation_pending" => false,
763 test "search by display name", %{conn: conn} do
764 user = insert(:user, name: "Display name")
767 conn = get(conn, "/api/pleroma/admin/users?name=display")
769 assert json_response(conn, 200) == %{
774 "deactivated" => user.deactivated,
776 "nickname" => user.nickname,
777 "roles" => %{"admin" => false, "moderator" => false},
780 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
781 "display_name" => HTML.strip_tags(user.name || user.nickname),
782 "confirmation_pending" => false,
789 test "search by email", %{conn: conn} do
790 user = insert(:user, email: "email@example.com")
793 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
795 assert json_response(conn, 200) == %{
800 "deactivated" => user.deactivated,
802 "nickname" => user.nickname,
803 "roles" => %{"admin" => false, "moderator" => false},
806 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
807 "display_name" => HTML.strip_tags(user.name || user.nickname),
808 "confirmation_pending" => false,
815 test "regular search with page size", %{conn: conn} do
816 user = insert(:user, nickname: "aalice")
817 user2 = insert(:user, nickname: "alice")
819 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
821 assert json_response(conn1, 200) == %{
826 "deactivated" => user.deactivated,
828 "nickname" => user.nickname,
829 "roles" => %{"admin" => false, "moderator" => false},
832 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
833 "display_name" => HTML.strip_tags(user.name || user.nickname),
834 "confirmation_pending" => false,
840 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
842 assert json_response(conn2, 200) == %{
847 "deactivated" => user2.deactivated,
849 "nickname" => user2.nickname,
850 "roles" => %{"admin" => false, "moderator" => false},
853 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
854 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
855 "confirmation_pending" => false,
862 test "only local users" do
863 admin = insert(:user, is_admin: true, nickname: "john")
864 token = insert(:oauth_admin_token, user: admin)
865 user = insert(:user, nickname: "bob")
867 insert(:user, nickname: "bobb", local: false)
871 |> assign(:user, admin)
872 |> assign(:token, token)
873 |> get("/api/pleroma/admin/users?query=bo&filters=local")
875 assert json_response(conn, 200) == %{
880 "deactivated" => user.deactivated,
882 "nickname" => user.nickname,
883 "roles" => %{"admin" => false, "moderator" => false},
886 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
887 "display_name" => HTML.strip_tags(user.name || user.nickname),
888 "confirmation_pending" => false,
895 test "only local users with no query", %{conn: conn, admin: old_admin} do
896 admin = insert(:user, is_admin: true, nickname: "john")
897 user = insert(:user, nickname: "bob")
899 insert(:user, nickname: "bobb", local: false)
901 conn = get(conn, "/api/pleroma/admin/users?filters=local")
906 "deactivated" => user.deactivated,
908 "nickname" => user.nickname,
909 "roles" => %{"admin" => false, "moderator" => false},
912 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
913 "display_name" => HTML.strip_tags(user.name || user.nickname),
914 "confirmation_pending" => false,
918 "deactivated" => admin.deactivated,
920 "nickname" => admin.nickname,
921 "roles" => %{"admin" => true, "moderator" => false},
924 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
925 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
926 "confirmation_pending" => false,
930 "deactivated" => false,
931 "id" => old_admin.id,
933 "nickname" => old_admin.nickname,
934 "roles" => %{"admin" => true, "moderator" => false},
936 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
937 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
938 "confirmation_pending" => false,
939 "url" => old_admin.ap_id
942 |> Enum.sort_by(& &1["nickname"])
944 assert json_response(conn, 200) == %{
951 test "load only admins", %{conn: conn, admin: admin} do
952 second_admin = insert(:user, is_admin: true)
956 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
961 "deactivated" => false,
963 "nickname" => admin.nickname,
964 "roles" => %{"admin" => true, "moderator" => false},
965 "local" => admin.local,
967 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
968 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
969 "confirmation_pending" => false,
973 "deactivated" => false,
974 "id" => second_admin.id,
975 "nickname" => second_admin.nickname,
976 "roles" => %{"admin" => true, "moderator" => false},
977 "local" => second_admin.local,
979 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
980 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
981 "confirmation_pending" => false,
982 "url" => second_admin.ap_id
985 |> Enum.sort_by(& &1["nickname"])
987 assert json_response(conn, 200) == %{
994 test "load only moderators", %{conn: conn} do
995 moderator = insert(:user, is_moderator: true)
999 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1001 assert json_response(conn, 200) == %{
1006 "deactivated" => false,
1007 "id" => moderator.id,
1008 "nickname" => moderator.nickname,
1009 "roles" => %{"admin" => false, "moderator" => true},
1010 "local" => moderator.local,
1012 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1013 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1014 "confirmation_pending" => false,
1015 "url" => moderator.ap_id
1021 test "load users with tags list", %{conn: conn} do
1022 user1 = insert(:user, tags: ["first"])
1023 user2 = insert(:user, tags: ["second"])
1027 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1032 "deactivated" => false,
1034 "nickname" => user1.nickname,
1035 "roles" => %{"admin" => false, "moderator" => false},
1036 "local" => user1.local,
1037 "tags" => ["first"],
1038 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1039 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1040 "confirmation_pending" => false,
1041 "url" => user1.ap_id
1044 "deactivated" => false,
1046 "nickname" => user2.nickname,
1047 "roles" => %{"admin" => false, "moderator" => false},
1048 "local" => user2.local,
1049 "tags" => ["second"],
1050 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1051 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1052 "confirmation_pending" => false,
1053 "url" => user2.ap_id
1056 |> Enum.sort_by(& &1["nickname"])
1058 assert json_response(conn, 200) == %{
1065 test "it works with multiple filters" do
1066 admin = insert(:user, nickname: "john", is_admin: true)
1067 token = insert(:oauth_admin_token, user: admin)
1068 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1070 insert(:user, nickname: "ken", local: true, deactivated: true)
1071 insert(:user, nickname: "bobb", local: false, deactivated: false)
1075 |> assign(:user, admin)
1076 |> assign(:token, token)
1077 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1079 assert json_response(conn, 200) == %{
1084 "deactivated" => user.deactivated,
1086 "nickname" => user.nickname,
1087 "roles" => %{"admin" => false, "moderator" => false},
1088 "local" => user.local,
1090 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1091 "display_name" => HTML.strip_tags(user.name || user.nickname),
1092 "confirmation_pending" => false,
1099 test "it omits relay user", %{admin: admin, conn: conn} do
1100 assert %User{} = Relay.get_actor()
1102 conn = get(conn, "/api/pleroma/admin/users")
1104 assert json_response(conn, 200) == %{
1109 "deactivated" => admin.deactivated,
1111 "nickname" => admin.nickname,
1112 "roles" => %{"admin" => true, "moderator" => false},
1115 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1116 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1117 "confirmation_pending" => false,
1118 "url" => admin.ap_id
1125 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1126 user_one = insert(:user, deactivated: true)
1127 user_two = insert(:user, deactivated: true)
1132 "/api/pleroma/admin/users/activate",
1133 %{nicknames: [user_one.nickname, user_two.nickname]}
1136 response = json_response(conn, 200)
1137 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1139 log_entry = Repo.one(ModerationLog)
1141 assert ModerationLog.get_log_entry_message(log_entry) ==
1142 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1145 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1146 user_one = insert(:user, deactivated: false)
1147 user_two = insert(:user, deactivated: false)
1152 "/api/pleroma/admin/users/deactivate",
1153 %{nicknames: [user_one.nickname, user_two.nickname]}
1156 response = json_response(conn, 200)
1157 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1159 log_entry = Repo.one(ModerationLog)
1161 assert ModerationLog.get_log_entry_message(log_entry) ==
1162 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1165 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1166 user = insert(:user)
1168 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1170 assert json_response(conn, 200) ==
1172 "deactivated" => !user.deactivated,
1174 "nickname" => user.nickname,
1175 "roles" => %{"admin" => false, "moderator" => false},
1178 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1179 "display_name" => HTML.strip_tags(user.name || user.nickname),
1180 "confirmation_pending" => false,
1184 log_entry = Repo.one(ModerationLog)
1186 assert ModerationLog.get_log_entry_message(log_entry) ==
1187 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1190 describe "PUT disable_mfa" do
1191 test "returns 200 and disable 2fa", %{conn: conn} do
1194 multi_factor_authentication_settings: %MFA.Settings{
1196 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1202 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1203 |> json_response(200)
1205 assert response == user.nickname
1206 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1208 refute mfa_settings.enabled
1209 refute mfa_settings.totp.confirmed
1212 test "returns 404 if user not found", %{conn: conn} do
1215 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1216 |> json_response(404)
1218 assert response == %{"error" => "Not found"}
1222 describe "GET /api/pleroma/admin/restart" do
1223 setup do: clear_config(:configurable_from_database, true)
1225 test "pleroma restarts", %{conn: conn} do
1227 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1228 end) =~ "pleroma restarted"
1230 refute Restarter.Pleroma.need_reboot?()
1234 test "need_reboot flag", %{conn: conn} do
1236 |> get("/api/pleroma/admin/need_reboot")
1237 |> json_response(200) == %{"need_reboot" => false}
1239 Restarter.Pleroma.need_reboot()
1242 |> get("/api/pleroma/admin/need_reboot")
1243 |> json_response(200) == %{"need_reboot" => true}
1245 on_exit(fn -> Restarter.Pleroma.refresh() end)
1248 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
1250 user = insert(:user)
1252 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1253 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1254 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1256 insert(:note_activity, user: user, published: date1)
1257 insert(:note_activity, user: user, published: date2)
1258 insert(:note_activity, user: user, published: date3)
1263 test "renders user's statuses", %{conn: conn, user: user} do
1264 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1266 assert json_response(conn, 200) |> length() == 3
1269 test "renders user's statuses with a limit", %{conn: conn, user: user} do
1270 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
1272 assert json_response(conn, 200) |> length() == 2
1275 test "doesn't return private statuses by default", %{conn: conn, user: user} do
1276 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1278 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1280 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
1282 assert json_response(conn, 200) |> length() == 4
1285 test "returns private statuses with godmode on", %{conn: conn, user: user} do
1286 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
1288 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
1290 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
1292 assert json_response(conn, 200) |> length() == 5
1295 test "excludes reblogs by default", %{conn: conn, user: user} do
1296 other_user = insert(:user)
1297 {:ok, activity} = CommonAPI.post(user, %{status: "."})
1298 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
1300 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
1301 assert json_response(conn_res, 200) |> length() == 0
1304 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
1306 assert json_response(conn_res, 200) |> length() == 1
1310 describe "GET /api/pleroma/admin/moderation_log" do
1312 moderator = insert(:user, is_moderator: true)
1314 %{moderator: moderator}
1317 test "returns the log", %{conn: conn, admin: admin} do
1318 Repo.insert(%ModerationLog{
1322 "nickname" => admin.nickname,
1325 action: "relay_follow",
1326 target: "https://example.org/relay"
1328 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1331 Repo.insert(%ModerationLog{
1335 "nickname" => admin.nickname,
1338 action: "relay_unfollow",
1339 target: "https://example.org/relay"
1341 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1344 conn = get(conn, "/api/pleroma/admin/moderation_log")
1346 response = json_response(conn, 200)
1347 [first_entry, second_entry] = response["items"]
1349 assert response["total"] == 2
1350 assert first_entry["data"]["action"] == "relay_unfollow"
1352 assert first_entry["message"] ==
1353 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1355 assert second_entry["data"]["action"] == "relay_follow"
1357 assert second_entry["message"] ==
1358 "@#{admin.nickname} followed relay: https://example.org/relay"
1361 test "returns the log with pagination", %{conn: conn, admin: admin} do
1362 Repo.insert(%ModerationLog{
1366 "nickname" => admin.nickname,
1369 action: "relay_follow",
1370 target: "https://example.org/relay"
1372 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
1375 Repo.insert(%ModerationLog{
1379 "nickname" => admin.nickname,
1382 action: "relay_unfollow",
1383 target: "https://example.org/relay"
1385 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
1388 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
1390 response1 = json_response(conn1, 200)
1391 [first_entry] = response1["items"]
1393 assert response1["total"] == 2
1394 assert response1["items"] |> length() == 1
1395 assert first_entry["data"]["action"] == "relay_unfollow"
1397 assert first_entry["message"] ==
1398 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1400 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
1402 response2 = json_response(conn2, 200)
1403 [second_entry] = response2["items"]
1405 assert response2["total"] == 2
1406 assert response2["items"] |> length() == 1
1407 assert second_entry["data"]["action"] == "relay_follow"
1409 assert second_entry["message"] ==
1410 "@#{admin.nickname} followed relay: https://example.org/relay"
1413 test "filters log by date", %{conn: conn, admin: admin} do
1414 first_date = "2017-08-15T15:47:06Z"
1415 second_date = "2017-08-20T15:47:06Z"
1417 Repo.insert(%ModerationLog{
1421 "nickname" => admin.nickname,
1424 action: "relay_follow",
1425 target: "https://example.org/relay"
1427 inserted_at: NaiveDateTime.from_iso8601!(first_date)
1430 Repo.insert(%ModerationLog{
1434 "nickname" => admin.nickname,
1437 action: "relay_unfollow",
1438 target: "https://example.org/relay"
1440 inserted_at: NaiveDateTime.from_iso8601!(second_date)
1446 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
1449 response1 = json_response(conn1, 200)
1450 [first_entry] = response1["items"]
1452 assert response1["total"] == 1
1453 assert first_entry["data"]["action"] == "relay_unfollow"
1455 assert first_entry["message"] ==
1456 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
1459 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
1460 Repo.insert(%ModerationLog{
1464 "nickname" => admin.nickname,
1467 action: "relay_follow",
1468 target: "https://example.org/relay"
1472 Repo.insert(%ModerationLog{
1475 "id" => moderator.id,
1476 "nickname" => moderator.nickname,
1479 action: "relay_unfollow",
1480 target: "https://example.org/relay"
1484 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
1486 response1 = json_response(conn1, 200)
1487 [first_entry] = response1["items"]
1489 assert response1["total"] == 1
1490 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
1493 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
1494 ModerationLog.insert_log(%{
1496 action: "relay_follow",
1497 target: "https://example.org/relay"
1500 ModerationLog.insert_log(%{
1502 action: "relay_unfollow",
1503 target: "https://example.org/relay"
1506 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
1508 response1 = json_response(conn1, 200)
1509 [first_entry] = response1["items"]
1511 assert response1["total"] == 1
1513 assert get_in(first_entry, ["data", "message"]) ==
1514 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
1518 test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated",
1520 clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated)
1521 user = insert(:user, %{local: false, nickname: "u@peer1.com"})
1522 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1524 assert json_response(conn, 200)
1527 describe "GET /users/:nickname/credentials" do
1528 test "gets the user credentials", %{conn: conn} do
1529 user = insert(:user)
1530 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
1532 response = assert json_response(conn, 200)
1533 assert response["email"] == user.email
1536 test "returns 403 if requested by a non-admin" do
1537 user = insert(:user)
1541 |> assign(:user, user)
1542 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
1544 assert json_response(conn, :forbidden)
1548 describe "PATCH /users/:nickname/credentials" do
1550 user = insert(:user)
1554 test "changes password and email", %{conn: conn, admin: admin, user: user} do
1555 assert user.password_reset_pending == false
1558 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1559 "password" => "new_password",
1560 "email" => "new_email@example.com",
1561 "name" => "new_name"
1564 assert json_response(conn, 200) == %{"status" => "success"}
1566 ObanHelpers.perform_all()
1568 updated_user = User.get_by_id(user.id)
1570 assert updated_user.email == "new_email@example.com"
1571 assert updated_user.name == "new_name"
1572 assert updated_user.password_hash != user.password_hash
1573 assert updated_user.password_reset_pending == true
1575 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
1577 assert ModerationLog.get_log_entry_message(log_entry1) ==
1578 "@#{admin.nickname} updated users: @#{user.nickname}"
1580 assert ModerationLog.get_log_entry_message(log_entry2) ==
1581 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
1584 test "returns 403 if requested by a non-admin", %{user: user} do
1587 |> assign(:user, user)
1588 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1589 "password" => "new_password",
1590 "email" => "new_email@example.com",
1591 "name" => "new_name"
1594 assert json_response(conn, :forbidden)
1597 test "changes actor type from permitted list", %{conn: conn, user: user} do
1598 assert user.actor_type == "Person"
1600 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1601 "actor_type" => "Service"
1603 |> json_response(200) == %{"status" => "success"}
1605 updated_user = User.get_by_id(user.id)
1607 assert updated_user.actor_type == "Service"
1609 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
1610 "actor_type" => "Application"
1612 |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}
1615 test "update non existing user", %{conn: conn} do
1616 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
1617 "password" => "new_password"
1619 |> json_response(404) == %{"error" => "Not found"}
1623 describe "PATCH /users/:nickname/force_password_reset" do
1624 test "sets password_reset_pending to true", %{conn: conn} do
1625 user = insert(:user)
1626 assert user.password_reset_pending == false
1629 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
1631 assert json_response(conn, 204) == ""
1633 ObanHelpers.perform_all()
1635 assert User.get_by_id(user.id).password_reset_pending == true
1639 describe "instances" do
1640 test "GET /instances/:instance/statuses", %{conn: conn} do
1641 user = insert(:user, local: false, nickname: "archaeme@archae.me")
1642 user2 = insert(:user, local: false, nickname: "test@test.com")
1643 insert_pair(:note_activity, user: user)
1644 activity = insert(:note_activity, user: user2)
1646 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1648 response = json_response(ret_conn, 200)
1650 assert length(response) == 2
1652 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
1654 response = json_response(ret_conn, 200)
1656 assert length(response) == 1
1658 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
1660 response = json_response(ret_conn, 200)
1662 assert Enum.empty?(response)
1664 CommonAPI.repeat(activity.id, user)
1666 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
1667 response = json_response(ret_conn, 200)
1668 assert length(response) == 2
1670 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
1671 response = json_response(ret_conn, 200)
1672 assert length(response) == 3
1676 describe "PATCH /confirm_email" do
1677 test "it confirms emails of two users", %{conn: conn, admin: admin} do
1678 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1680 assert first_user.confirmation_pending == true
1681 assert second_user.confirmation_pending == true
1684 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
1686 first_user.nickname,
1687 second_user.nickname
1691 assert ret_conn.status == 200
1693 assert first_user.confirmation_pending == true
1694 assert second_user.confirmation_pending == true
1696 log_entry = Repo.one(ModerationLog)
1698 assert ModerationLog.get_log_entry_message(log_entry) ==
1699 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
1700 second_user.nickname
1705 describe "PATCH /resend_confirmation_email" do
1706 test "it resend emails for two users", %{conn: conn, admin: admin} do
1707 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
1710 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
1712 first_user.nickname,
1713 second_user.nickname
1717 assert ret_conn.status == 200
1719 log_entry = Repo.one(ModerationLog)
1721 assert ModerationLog.get_log_entry_message(log_entry) ==
1722 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
1723 second_user.nickname
1726 ObanHelpers.perform_all()
1727 assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(first_user))
1731 describe "/api/pleroma/admin/stats" do
1732 test "status visibility count", %{conn: conn} do
1733 admin = insert(:user, is_admin: true)
1734 user = insert(:user)
1735 CommonAPI.post(user, %{visibility: "public", status: "hey"})
1736 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1737 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1741 |> assign(:user, admin)
1742 |> get("/api/pleroma/admin/stats")
1743 |> json_response(200)
1745 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
1746 response["status_visibility"]
1749 test "by instance", %{conn: conn} do
1750 admin = insert(:user, is_admin: true)
1751 user1 = insert(:user)
1752 instance2 = "instance2.tld"
1753 user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
1755 CommonAPI.post(user1, %{visibility: "public", status: "hey"})
1756 CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"})
1757 CommonAPI.post(user2, %{visibility: "private", status: "hey"})
1761 |> assign(:user, admin)
1762 |> get("/api/pleroma/admin/stats", instance: instance2)
1763 |> json_response(200)
1765 assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
1766 response["status_visibility"]
1771 # Needed for testing
1772 defmodule Pleroma.Web.Endpoint.NotReal do
1775 defmodule Pleroma.Captcha.NotReal do