1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
6 use Pleroma.Web.ConnCase
7 use Oban.Testing, repo: Pleroma.Repo
9 import ExUnit.CaptureLog
11 import Pleroma.Factory
13 alias Pleroma.Activity
15 alias Pleroma.ConfigDB
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
344 assert expected == json_response(conn, 200)
347 test "when the user doesn't exist", %{conn: conn} do
350 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
352 assert %{"error" => "Not found"} == json_response(conn, 404)
356 describe "/api/pleroma/admin/users/follow" do
357 test "allows to force-follow another user", %{admin: admin, conn: conn} do
359 follower = insert(:user)
362 |> put_req_header("accept", "application/json")
363 |> post("/api/pleroma/admin/users/follow", %{
364 "follower" => follower.nickname,
365 "followed" => user.nickname
368 user = User.get_cached_by_id(user.id)
369 follower = User.get_cached_by_id(follower.id)
371 assert User.following?(follower, user)
373 log_entry = Repo.one(ModerationLog)
375 assert ModerationLog.get_log_entry_message(log_entry) ==
376 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
380 describe "/api/pleroma/admin/users/unfollow" do
381 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
383 follower = insert(:user)
385 User.follow(follower, user)
388 |> put_req_header("accept", "application/json")
389 |> post("/api/pleroma/admin/users/unfollow", %{
390 "follower" => follower.nickname,
391 "followed" => user.nickname
394 user = User.get_cached_by_id(user.id)
395 follower = User.get_cached_by_id(follower.id)
397 refute User.following?(follower, user)
399 log_entry = Repo.one(ModerationLog)
401 assert ModerationLog.get_log_entry_message(log_entry) ==
402 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
406 describe "PUT /api/pleroma/admin/users/tag" do
407 setup %{conn: conn} do
408 user1 = insert(:user, %{tags: ["x"]})
409 user2 = insert(:user, %{tags: ["y"]})
410 user3 = insert(:user, %{tags: ["unchanged"]})
414 |> put_req_header("accept", "application/json")
416 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
417 "#{user2.nickname}&tags[]=foo&tags[]=bar"
420 %{conn: conn, user1: user1, user2: user2, user3: user3}
423 test "it appends specified tags to users with specified nicknames", %{
429 assert json_response(conn, :no_content)
430 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
431 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
433 log_entry = Repo.one(ModerationLog)
436 [user1.nickname, user2.nickname]
437 |> Enum.map(&"@#{&1}")
440 tags = ["foo", "bar"] |> Enum.join(", ")
442 assert ModerationLog.get_log_entry_message(log_entry) ==
443 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
446 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
447 assert json_response(conn, :no_content)
448 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
452 describe "DELETE /api/pleroma/admin/users/tag" do
453 setup %{conn: conn} do
454 user1 = insert(:user, %{tags: ["x"]})
455 user2 = insert(:user, %{tags: ["y", "z"]})
456 user3 = insert(:user, %{tags: ["unchanged"]})
460 |> put_req_header("accept", "application/json")
462 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
463 "#{user2.nickname}&tags[]=x&tags[]=z"
466 %{conn: conn, user1: user1, user2: user2, user3: user3}
469 test "it removes specified tags from users with specified nicknames", %{
475 assert json_response(conn, :no_content)
476 assert User.get_cached_by_id(user1.id).tags == []
477 assert User.get_cached_by_id(user2.id).tags == ["y"]
479 log_entry = Repo.one(ModerationLog)
482 [user1.nickname, user2.nickname]
483 |> Enum.map(&"@#{&1}")
486 tags = ["x", "z"] |> Enum.join(", ")
488 assert ModerationLog.get_log_entry_message(log_entry) ==
489 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
492 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
493 assert json_response(conn, :no_content)
494 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
498 describe "/api/pleroma/admin/users/:nickname/permission_group" do
499 test "GET is giving user_info", %{admin: admin, conn: conn} do
502 |> put_req_header("accept", "application/json")
503 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
505 assert json_response(conn, 200) == %{
507 "is_moderator" => false
511 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
516 |> put_req_header("accept", "application/json")
517 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
519 assert json_response(conn, 200) == %{
523 log_entry = Repo.one(ModerationLog)
525 assert ModerationLog.get_log_entry_message(log_entry) ==
526 "@#{admin.nickname} made @#{user.nickname} admin"
529 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
530 user_one = insert(:user)
531 user_two = insert(:user)
535 |> put_req_header("accept", "application/json")
536 |> post("/api/pleroma/admin/users/permission_group/admin", %{
537 nicknames: [user_one.nickname, user_two.nickname]
540 assert json_response(conn, 200) == %{"is_admin" => true}
542 log_entry = Repo.one(ModerationLog)
544 assert ModerationLog.get_log_entry_message(log_entry) ==
545 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
548 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
549 user = insert(:user, is_admin: true)
553 |> put_req_header("accept", "application/json")
554 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
556 assert json_response(conn, 200) == %{"is_admin" => false}
558 log_entry = Repo.one(ModerationLog)
560 assert ModerationLog.get_log_entry_message(log_entry) ==
561 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
564 test "/:right DELETE, can remove from a permission group (multiple)", %{
568 user_one = insert(:user, is_admin: true)
569 user_two = insert(:user, is_admin: true)
573 |> put_req_header("accept", "application/json")
574 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
575 nicknames: [user_one.nickname, user_two.nickname]
578 assert json_response(conn, 200) == %{"is_admin" => false}
580 log_entry = Repo.one(ModerationLog)
582 assert ModerationLog.get_log_entry_message(log_entry) ==
583 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
589 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
594 |> put_req_header("accept", "application/json")
595 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
597 resp = json_response(conn, 200)
599 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
602 describe "GET /api/pleroma/admin/users" do
603 test "renders users array for the first page", %{conn: conn, admin: admin} do
604 user = insert(:user, local: false, tags: ["foo", "bar"])
605 conn = get(conn, "/api/pleroma/admin/users?page=1")
610 "deactivated" => admin.deactivated,
612 "nickname" => admin.nickname,
613 "roles" => %{"admin" => true, "moderator" => false},
616 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
617 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
618 "confirmation_pending" => false
621 "deactivated" => user.deactivated,
623 "nickname" => user.nickname,
624 "roles" => %{"admin" => false, "moderator" => false},
626 "tags" => ["foo", "bar"],
627 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
628 "display_name" => HTML.strip_tags(user.name || user.nickname),
629 "confirmation_pending" => false
632 |> Enum.sort_by(& &1["nickname"])
634 assert json_response(conn, 200) == %{
641 test "pagination works correctly with service users", %{conn: conn} do
642 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
644 insert_list(25, :user)
646 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
648 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
649 |> json_response(200)
651 assert Enum.count(users1) == 10
652 assert service1 not in users1
654 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
656 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
657 |> json_response(200)
659 assert Enum.count(users2) == 10
660 assert service1 not in users2
662 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
664 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
665 |> json_response(200)
667 assert Enum.count(users3) == 6
668 assert service1 not in users3
671 test "renders empty array for the second page", %{conn: conn} do
674 conn = get(conn, "/api/pleroma/admin/users?page=2")
676 assert json_response(conn, 200) == %{
683 test "regular search", %{conn: conn} do
684 user = insert(:user, nickname: "bob")
686 conn = get(conn, "/api/pleroma/admin/users?query=bo")
688 assert json_response(conn, 200) == %{
693 "deactivated" => user.deactivated,
695 "nickname" => user.nickname,
696 "roles" => %{"admin" => false, "moderator" => false},
699 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
700 "display_name" => HTML.strip_tags(user.name || user.nickname),
701 "confirmation_pending" => false
707 test "search by domain", %{conn: conn} do
708 user = insert(:user, nickname: "nickname@domain.com")
711 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
713 assert json_response(conn, 200) == %{
718 "deactivated" => user.deactivated,
720 "nickname" => user.nickname,
721 "roles" => %{"admin" => false, "moderator" => false},
724 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
725 "display_name" => HTML.strip_tags(user.name || user.nickname),
726 "confirmation_pending" => false
732 test "search by full nickname", %{conn: conn} do
733 user = insert(:user, nickname: "nickname@domain.com")
736 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
738 assert json_response(conn, 200) == %{
743 "deactivated" => user.deactivated,
745 "nickname" => user.nickname,
746 "roles" => %{"admin" => false, "moderator" => false},
749 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
750 "display_name" => HTML.strip_tags(user.name || user.nickname),
751 "confirmation_pending" => false
757 test "search by display name", %{conn: conn} do
758 user = insert(:user, name: "Display name")
761 conn = get(conn, "/api/pleroma/admin/users?name=display")
763 assert json_response(conn, 200) == %{
768 "deactivated" => user.deactivated,
770 "nickname" => user.nickname,
771 "roles" => %{"admin" => false, "moderator" => false},
774 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
775 "display_name" => HTML.strip_tags(user.name || user.nickname),
776 "confirmation_pending" => false
782 test "search by email", %{conn: conn} do
783 user = insert(:user, email: "email@example.com")
786 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
788 assert json_response(conn, 200) == %{
793 "deactivated" => user.deactivated,
795 "nickname" => user.nickname,
796 "roles" => %{"admin" => false, "moderator" => false},
799 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
800 "display_name" => HTML.strip_tags(user.name || user.nickname),
801 "confirmation_pending" => false
807 test "regular search with page size", %{conn: conn} do
808 user = insert(:user, nickname: "aalice")
809 user2 = insert(:user, nickname: "alice")
811 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
813 assert json_response(conn1, 200) == %{
818 "deactivated" => user.deactivated,
820 "nickname" => user.nickname,
821 "roles" => %{"admin" => false, "moderator" => false},
824 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
825 "display_name" => HTML.strip_tags(user.name || user.nickname),
826 "confirmation_pending" => false
831 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
833 assert json_response(conn2, 200) == %{
838 "deactivated" => user2.deactivated,
840 "nickname" => user2.nickname,
841 "roles" => %{"admin" => false, "moderator" => false},
844 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
845 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
846 "confirmation_pending" => false
852 test "only local users" do
853 admin = insert(:user, is_admin: true, nickname: "john")
854 token = insert(:oauth_admin_token, user: admin)
855 user = insert(:user, nickname: "bob")
857 insert(:user, nickname: "bobb", local: false)
861 |> assign(:user, admin)
862 |> assign(:token, token)
863 |> get("/api/pleroma/admin/users?query=bo&filters=local")
865 assert json_response(conn, 200) == %{
870 "deactivated" => user.deactivated,
872 "nickname" => user.nickname,
873 "roles" => %{"admin" => false, "moderator" => false},
876 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
877 "display_name" => HTML.strip_tags(user.name || user.nickname),
878 "confirmation_pending" => false
884 test "only local users with no query", %{conn: conn, admin: old_admin} do
885 admin = insert(:user, is_admin: true, nickname: "john")
886 user = insert(:user, nickname: "bob")
888 insert(:user, nickname: "bobb", local: false)
890 conn = get(conn, "/api/pleroma/admin/users?filters=local")
895 "deactivated" => user.deactivated,
897 "nickname" => user.nickname,
898 "roles" => %{"admin" => false, "moderator" => false},
901 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
902 "display_name" => HTML.strip_tags(user.name || user.nickname),
903 "confirmation_pending" => false
906 "deactivated" => admin.deactivated,
908 "nickname" => admin.nickname,
909 "roles" => %{"admin" => true, "moderator" => false},
912 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
913 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
914 "confirmation_pending" => false
917 "deactivated" => false,
918 "id" => old_admin.id,
920 "nickname" => old_admin.nickname,
921 "roles" => %{"admin" => true, "moderator" => false},
923 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
924 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
925 "confirmation_pending" => false
928 |> Enum.sort_by(& &1["nickname"])
930 assert json_response(conn, 200) == %{
937 test "load only admins", %{conn: conn, admin: admin} do
938 second_admin = insert(:user, is_admin: true)
942 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
947 "deactivated" => false,
949 "nickname" => admin.nickname,
950 "roles" => %{"admin" => true, "moderator" => false},
951 "local" => admin.local,
953 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
954 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
955 "confirmation_pending" => false
958 "deactivated" => false,
959 "id" => second_admin.id,
960 "nickname" => second_admin.nickname,
961 "roles" => %{"admin" => true, "moderator" => false},
962 "local" => second_admin.local,
964 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
965 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
966 "confirmation_pending" => false
969 |> Enum.sort_by(& &1["nickname"])
971 assert json_response(conn, 200) == %{
978 test "load only moderators", %{conn: conn} do
979 moderator = insert(:user, is_moderator: true)
983 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
985 assert json_response(conn, 200) == %{
990 "deactivated" => false,
991 "id" => moderator.id,
992 "nickname" => moderator.nickname,
993 "roles" => %{"admin" => false, "moderator" => true},
994 "local" => moderator.local,
996 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
997 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
998 "confirmation_pending" => false
1004 test "load users with tags list", %{conn: conn} do
1005 user1 = insert(:user, tags: ["first"])
1006 user2 = insert(:user, tags: ["second"])
1010 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1015 "deactivated" => false,
1017 "nickname" => user1.nickname,
1018 "roles" => %{"admin" => false, "moderator" => false},
1019 "local" => user1.local,
1020 "tags" => ["first"],
1021 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1022 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1023 "confirmation_pending" => false
1026 "deactivated" => false,
1028 "nickname" => user2.nickname,
1029 "roles" => %{"admin" => false, "moderator" => false},
1030 "local" => user2.local,
1031 "tags" => ["second"],
1032 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1033 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1034 "confirmation_pending" => false
1037 |> Enum.sort_by(& &1["nickname"])
1039 assert json_response(conn, 200) == %{
1046 test "it works with multiple filters" do
1047 admin = insert(:user, nickname: "john", is_admin: true)
1048 token = insert(:oauth_admin_token, user: admin)
1049 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1051 insert(:user, nickname: "ken", local: true, deactivated: true)
1052 insert(:user, nickname: "bobb", local: false, deactivated: false)
1056 |> assign(:user, admin)
1057 |> assign(:token, token)
1058 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1060 assert json_response(conn, 200) == %{
1065 "deactivated" => user.deactivated,
1067 "nickname" => user.nickname,
1068 "roles" => %{"admin" => false, "moderator" => false},
1069 "local" => user.local,
1071 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1072 "display_name" => HTML.strip_tags(user.name || user.nickname),
1073 "confirmation_pending" => false
1079 test "it omits relay user", %{admin: admin, conn: conn} do
1080 assert %User{} = Relay.get_actor()
1082 conn = get(conn, "/api/pleroma/admin/users")
1084 assert json_response(conn, 200) == %{
1089 "deactivated" => admin.deactivated,
1091 "nickname" => admin.nickname,
1092 "roles" => %{"admin" => true, "moderator" => false},
1095 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1096 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1097 "confirmation_pending" => false
1104 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1105 user_one = insert(:user, deactivated: true)
1106 user_two = insert(:user, deactivated: true)
1111 "/api/pleroma/admin/users/activate",
1112 %{nicknames: [user_one.nickname, user_two.nickname]}
1115 response = json_response(conn, 200)
1116 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1118 log_entry = Repo.one(ModerationLog)
1120 assert ModerationLog.get_log_entry_message(log_entry) ==
1121 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1124 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1125 user_one = insert(:user, deactivated: false)
1126 user_two = insert(:user, deactivated: false)
1131 "/api/pleroma/admin/users/deactivate",
1132 %{nicknames: [user_one.nickname, user_two.nickname]}
1135 response = json_response(conn, 200)
1136 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1138 log_entry = Repo.one(ModerationLog)
1140 assert ModerationLog.get_log_entry_message(log_entry) ==
1141 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1144 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1145 user = insert(:user)
1147 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1149 assert json_response(conn, 200) ==
1151 "deactivated" => !user.deactivated,
1153 "nickname" => user.nickname,
1154 "roles" => %{"admin" => false, "moderator" => false},
1157 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1158 "display_name" => HTML.strip_tags(user.name || user.nickname),
1159 "confirmation_pending" => false
1162 log_entry = Repo.one(ModerationLog)
1164 assert ModerationLog.get_log_entry_message(log_entry) ==
1165 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1168 describe "PUT disable_mfa" do
1169 test "returns 200 and disable 2fa", %{conn: conn} do
1172 multi_factor_authentication_settings: %MFA.Settings{
1174 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1180 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1181 |> json_response(200)
1183 assert response == user.nickname
1184 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1186 refute mfa_settings.enabled
1187 refute mfa_settings.totp.confirmed
1190 test "returns 404 if user not found", %{conn: conn} do
1193 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1194 |> json_response(404)
1196 assert response == %{"error" => "Not found"}
1200 describe "GET /api/pleroma/admin/config" do
1201 setup do: clear_config(:configurable_from_database, true)
1203 test "when configuration from database is off", %{conn: conn} do
1204 Config.put(:configurable_from_database, false)
1205 conn = get(conn, "/api/pleroma/admin/config")
1207 assert json_response(conn, 400) ==
1209 "error" => "To use this endpoint you need to enable configuration from database."
1213 test "with settings only in db", %{conn: conn} do
1214 config1 = insert(:config)
1215 config2 = insert(:config)
1217 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1222 "group" => ":pleroma",
1227 "group" => ":pleroma",
1232 } = json_response(conn, 200)
1234 assert key1 == config1.key
1235 assert key2 == config2.key
1238 test "db is added to settings that are in db", %{conn: conn} do
1239 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1241 %{"configs" => configs} =
1243 |> get("/api/pleroma/admin/config")
1244 |> json_response(200)
1247 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1248 group == ":pleroma" and key == ":instance"
1251 assert instance_config["db"] == [":name"]
1254 test "merged default setting with db settings", %{conn: conn} do
1255 config1 = insert(:config)
1256 config2 = insert(:config)
1260 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1263 %{"configs" => configs} =
1265 |> get("/api/pleroma/admin/config")
1266 |> json_response(200)
1268 assert length(configs) > 3
1271 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1272 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1275 assert length(received_configs) == 3
1279 |> ConfigDB.from_binary()
1281 |> ConfigDB.convert()
1283 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1284 assert db in [[config1.key], [config2.key], db_keys]
1287 ConfigDB.from_binary_with_convert(config1.value),
1288 ConfigDB.from_binary_with_convert(config2.value),
1289 ConfigDB.from_binary_with_convert(config3.value)
1294 test "subkeys with full update right merge", %{conn: conn} do
1298 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1304 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1307 %{"configs" => configs} =
1309 |> get("/api/pleroma/admin/config")
1310 |> json_response(200)
1313 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1314 group == ":pleroma" and key in [config1.key, config2.key]
1317 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
1318 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
1320 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
1321 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
1323 assert emoji_val[:groups] == [a: 1, b: 2]
1324 assert assets_val[:mascots] == [a: 1, b: 2]
1328 test "POST /api/pleroma/admin/config error", %{conn: conn} do
1329 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
1331 assert json_response(conn, 400) ==
1332 %{"error" => "To use this endpoint you need to enable configuration from database."}
1335 describe "POST /api/pleroma/admin/config" do
1337 http = Application.get_env(:pleroma, :http)
1340 Application.delete_env(:pleroma, :key1)
1341 Application.delete_env(:pleroma, :key2)
1342 Application.delete_env(:pleroma, :key3)
1343 Application.delete_env(:pleroma, :key4)
1344 Application.delete_env(:pleroma, :keyaa1)
1345 Application.delete_env(:pleroma, :keyaa2)
1346 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1347 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1348 Application.put_env(:pleroma, :http, http)
1349 Application.put_env(:tesla, :adapter, Tesla.Mock)
1350 Restarter.Pleroma.refresh()
1354 setup do: clear_config(:configurable_from_database, true)
1356 @tag capture_log: true
1357 test "create new config setting in db", %{conn: conn} do
1358 ueberauth = Application.get_env(:ueberauth, Ueberauth)
1359 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
1362 post(conn, "/api/pleroma/admin/config", %{
1364 %{group: ":pleroma", key: ":key1", value: "value1"},
1366 group: ":ueberauth",
1368 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1374 ":nested_1" => "nested_value1",
1376 %{":nested_22" => "nested_value222"},
1377 %{":nested_33" => %{":nested_44" => "nested_444"}}
1385 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1386 %{"nested_4" => true}
1392 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
1397 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
1402 assert json_response(conn, 200) == %{
1405 "group" => ":pleroma",
1407 "value" => "value1",
1411 "group" => ":ueberauth",
1412 "key" => "Ueberauth",
1413 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
1414 "db" => [":consumer_secret"]
1417 "group" => ":pleroma",
1420 ":nested_1" => "nested_value1",
1422 %{":nested_22" => "nested_value222"},
1423 %{":nested_33" => %{":nested_44" => "nested_444"}}
1429 "group" => ":pleroma",
1432 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1433 %{"nested_4" => true}
1438 "group" => ":pleroma",
1440 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
1446 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
1452 assert Application.get_env(:pleroma, :key1) == "value1"
1454 assert Application.get_env(:pleroma, :key2) == %{
1455 nested_1: "nested_value1",
1457 %{nested_22: "nested_value222"},
1458 %{nested_33: %{nested_44: "nested_444"}}
1462 assert Application.get_env(:pleroma, :key3) == [
1463 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
1464 %{"nested_4" => true}
1467 assert Application.get_env(:pleroma, :key4) == %{
1468 "endpoint" => "https://example.com",
1472 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
1475 test "save configs setting without explicit key", %{conn: conn} do
1476 level = Application.get_env(:quack, :level)
1477 meta = Application.get_env(:quack, :meta)
1478 webhook_url = Application.get_env(:quack, :webhook_url)
1481 Application.put_env(:quack, :level, level)
1482 Application.put_env(:quack, :meta, meta)
1483 Application.put_env(:quack, :webhook_url, webhook_url)
1487 post(conn, "/api/pleroma/admin/config", %{
1501 key: ":webhook_url",
1502 value: "https://hooks.slack.com/services/KEY"
1507 assert json_response(conn, 200) == %{
1510 "group" => ":quack",
1516 "group" => ":quack",
1518 "value" => [":none"],
1522 "group" => ":quack",
1523 "key" => ":webhook_url",
1524 "value" => "https://hooks.slack.com/services/KEY",
1525 "db" => [":webhook_url"]
1530 assert Application.get_env(:quack, :level) == :info
1531 assert Application.get_env(:quack, :meta) == [:none]
1532 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
1535 test "saving config with partial update", %{conn: conn} do
1536 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
1539 post(conn, "/api/pleroma/admin/config", %{
1541 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
1545 assert json_response(conn, 200) == %{
1548 "group" => ":pleroma",
1551 %{"tuple" => [":key1", 1]},
1552 %{"tuple" => [":key2", 2]},
1553 %{"tuple" => [":key3", 3]}
1555 "db" => [":key1", ":key2", ":key3"]
1561 test "saving config which need pleroma reboot", %{conn: conn} do
1562 chat = Config.get(:chat)
1563 on_exit(fn -> Config.put(:chat, chat) end)
1567 "/api/pleroma/admin/config",
1570 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
1574 |> json_response(200) == %{
1577 "db" => [":enabled"],
1578 "group" => ":pleroma",
1580 "value" => [%{"tuple" => [":enabled", true]}]
1583 "need_reboot" => true
1588 |> get("/api/pleroma/admin/config")
1589 |> json_response(200)
1591 assert configs["need_reboot"]
1594 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1595 end) =~ "pleroma restarted"
1599 |> get("/api/pleroma/admin/config")
1600 |> json_response(200)
1602 assert configs["need_reboot"] == false
1605 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
1606 chat = Config.get(:chat)
1607 on_exit(fn -> Config.put(:chat, chat) end)
1611 "/api/pleroma/admin/config",
1614 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
1618 |> json_response(200) == %{
1621 "db" => [":enabled"],
1622 "group" => ":pleroma",
1624 "value" => [%{"tuple" => [":enabled", true]}]
1627 "need_reboot" => true
1630 assert post(conn, "/api/pleroma/admin/config", %{
1632 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
1635 |> json_response(200) == %{
1638 "group" => ":pleroma",
1641 %{"tuple" => [":key3", 3]}
1646 "need_reboot" => true
1650 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
1651 end) =~ "pleroma restarted"
1655 |> get("/api/pleroma/admin/config")
1656 |> json_response(200)
1658 assert configs["need_reboot"] == false
1661 test "saving config with nested merge", %{conn: conn} do
1663 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
1666 post(conn, "/api/pleroma/admin/config", %{
1669 group: config.group,
1672 %{"tuple" => [":key3", 3]},
1677 %{"tuple" => [":k2", 1]},
1678 %{"tuple" => [":k3", 3]}
1687 assert json_response(conn, 200) == %{
1690 "group" => ":pleroma",
1693 %{"tuple" => [":key1", 1]},
1694 %{"tuple" => [":key3", 3]},
1699 %{"tuple" => [":k1", 1]},
1700 %{"tuple" => [":k2", 1]},
1701 %{"tuple" => [":k3", 3]}
1706 "db" => [":key1", ":key3", ":key2"]
1712 test "saving special atoms", %{conn: conn} do
1714 post(conn, "/api/pleroma/admin/config", %{
1717 "group" => ":pleroma",
1723 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
1731 assert json_response(conn, 200) == %{
1734 "group" => ":pleroma",
1740 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
1744 "db" => [":ssl_options"]
1749 assert Application.get_env(:pleroma, :key1) == [
1750 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
1754 test "saving full setting if value is in full_key_update list", %{conn: conn} do
1755 backends = Application.get_env(:logger, :backends)
1756 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
1762 value: :erlang.term_to_binary([])
1765 Pleroma.Config.TransferTask.load_and_update_env([], false)
1767 assert Application.get_env(:logger, :backends) == []
1770 post(conn, "/api/pleroma/admin/config", %{
1773 group: config.group,
1780 assert json_response(conn, 200) == %{
1783 "group" => ":logger",
1784 "key" => ":backends",
1788 "db" => [":backends"]
1793 assert Application.get_env(:logger, :backends) == [
1798 test "saving full setting if value is not keyword", %{conn: conn} do
1803 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
1807 post(conn, "/api/pleroma/admin/config", %{
1809 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
1813 assert json_response(conn, 200) == %{
1816 "group" => ":tesla",
1817 "key" => ":adapter",
1818 "value" => "Tesla.Adapter.Httpc",
1819 "db" => [":adapter"]
1825 test "update config setting & delete with fallback to default value", %{
1830 ueberauth = Application.get_env(:ueberauth, Ueberauth)
1831 config1 = insert(:config, key: ":keyaa1")
1832 config2 = insert(:config, key: ":keyaa2")
1836 group: ":ueberauth",
1841 post(conn, "/api/pleroma/admin/config", %{
1843 %{group: config1.group, key: config1.key, value: "another_value"},
1844 %{group: config2.group, key: config2.key, value: "another_value"}
1848 assert json_response(conn, 200) == %{
1851 "group" => ":pleroma",
1852 "key" => config1.key,
1853 "value" => "another_value",
1857 "group" => ":pleroma",
1858 "key" => config2.key,
1859 "value" => "another_value",
1865 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
1866 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
1867 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
1871 |> assign(:user, admin)
1872 |> assign(:token, token)
1873 |> post("/api/pleroma/admin/config", %{
1875 %{group: config2.group, key: config2.key, delete: true},
1877 group: ":ueberauth",
1884 assert json_response(conn, 200) == %{
1888 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
1889 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
1892 test "common config example", %{conn: conn} do
1894 post(conn, "/api/pleroma/admin/config", %{
1897 "group" => ":pleroma",
1898 "key" => "Pleroma.Captcha.NotReal",
1900 %{"tuple" => [":enabled", false]},
1901 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
1902 %{"tuple" => [":seconds_valid", 60]},
1903 %{"tuple" => [":path", ""]},
1904 %{"tuple" => [":key1", nil]},
1905 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
1906 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
1907 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
1908 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
1909 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
1910 %{"tuple" => [":name", "Pleroma"]}
1916 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
1918 assert json_response(conn, 200) == %{
1921 "group" => ":pleroma",
1922 "key" => "Pleroma.Captcha.NotReal",
1924 %{"tuple" => [":enabled", false]},
1925 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
1926 %{"tuple" => [":seconds_valid", 60]},
1927 %{"tuple" => [":path", ""]},
1928 %{"tuple" => [":key1", nil]},
1929 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
1930 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
1931 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
1932 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
1933 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
1934 %{"tuple" => [":name", "Pleroma"]}
1954 test "tuples with more than two values", %{conn: conn} do
1956 post(conn, "/api/pleroma/admin/config", %{
1959 "group" => ":pleroma",
1960 "key" => "Pleroma.Web.Endpoint.NotReal",
1976 "/api/v1/streaming",
1977 "Pleroma.Web.MastodonAPI.WebsocketHandler",
1984 "Phoenix.Endpoint.CowboyWebSocket",
1987 "Phoenix.Transports.WebSocket",
1990 "Pleroma.Web.Endpoint",
1991 "Pleroma.Web.UserSocket",
2002 "Phoenix.Endpoint.Cowboy2Handler",
2003 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2020 assert json_response(conn, 200) == %{
2023 "group" => ":pleroma",
2024 "key" => "Pleroma.Web.Endpoint.NotReal",
2040 "/api/v1/streaming",
2041 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2048 "Phoenix.Endpoint.CowboyWebSocket",
2051 "Phoenix.Transports.WebSocket",
2054 "Pleroma.Web.Endpoint",
2055 "Pleroma.Web.UserSocket",
2066 "Phoenix.Endpoint.Cowboy2Handler",
2067 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2086 test "settings with nesting map", %{conn: conn} do
2088 post(conn, "/api/pleroma/admin/config", %{
2091 "group" => ":pleroma",
2094 %{"tuple" => [":key2", "some_val"]},
2099 ":max_options" => 20,
2100 ":max_option_chars" => 200,
2101 ":min_expiration" => 0,
2102 ":max_expiration" => 31_536_000,
2104 ":max_options" => 20,
2105 ":max_option_chars" => 200,
2106 ":min_expiration" => 0,
2107 ":max_expiration" => 31_536_000
2117 assert json_response(conn, 200) ==
2121 "group" => ":pleroma",
2124 %{"tuple" => [":key2", "some_val"]},
2129 ":max_expiration" => 31_536_000,
2130 ":max_option_chars" => 200,
2131 ":max_options" => 20,
2132 ":min_expiration" => 0,
2134 ":max_expiration" => 31_536_000,
2135 ":max_option_chars" => 200,
2136 ":max_options" => 20,
2137 ":min_expiration" => 0
2143 "db" => [":key2", ":key3"]
2149 test "value as map", %{conn: conn} do
2151 post(conn, "/api/pleroma/admin/config", %{
2154 "group" => ":pleroma",
2156 "value" => %{"key" => "some_val"}
2161 assert json_response(conn, 200) ==
2165 "group" => ":pleroma",
2167 "value" => %{"key" => "some_val"},
2174 test "queues key as atom", %{conn: conn} do
2176 post(conn, "/api/pleroma/admin/config", %{
2182 %{"tuple" => [":federator_incoming", 50]},
2183 %{"tuple" => [":federator_outgoing", 50]},
2184 %{"tuple" => [":web_push", 50]},
2185 %{"tuple" => [":mailer", 10]},
2186 %{"tuple" => [":transmogrifier", 20]},
2187 %{"tuple" => [":scheduled_activities", 10]},
2188 %{"tuple" => [":background", 5]}
2194 assert json_response(conn, 200) == %{
2200 %{"tuple" => [":federator_incoming", 50]},
2201 %{"tuple" => [":federator_outgoing", 50]},
2202 %{"tuple" => [":web_push", 50]},
2203 %{"tuple" => [":mailer", 10]},
2204 %{"tuple" => [":transmogrifier", 20]},
2205 %{"tuple" => [":scheduled_activities", 10]},
2206 %{"tuple" => [":background", 5]}
2209 ":federator_incoming",
2210 ":federator_outgoing",
2214 ":scheduled_activities",
2222 test "delete part of settings by atom subkeys", %{conn: conn} do
2226 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2230 post(conn, "/api/pleroma/admin/config", %{
2233 group: config.group,
2235 subkeys: [":subkey1", ":subkey3"],
2241 assert json_response(conn, 200) == %{
2244 "group" => ":pleroma",
2246 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2247 "db" => [":subkey2"]
2253 test "proxy tuple localhost", %{conn: conn} do
2255 post(conn, "/api/pleroma/admin/config", %{
2261 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
2270 "group" => ":pleroma",
2276 } = json_response(conn, 200)
2278 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
2279 assert ":proxy_url" in db
2282 test "proxy tuple domain", %{conn: conn} do
2284 post(conn, "/api/pleroma/admin/config", %{
2290 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
2299 "group" => ":pleroma",
2305 } = json_response(conn, 200)
2307 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
2308 assert ":proxy_url" in db
2311 test "proxy tuple ip", %{conn: conn} do
2313 post(conn, "/api/pleroma/admin/config", %{
2319 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
2328 "group" => ":pleroma",
2334 } = json_response(conn, 200)
2336 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
2337 assert ":proxy_url" in db
2340 @tag capture_log: true
2341 test "doesn't set keys not in the whitelist", %{conn: conn} do
2342 clear_config(:database_config_whitelist, [
2345 {:pleroma, Pleroma.Captcha.NotReal},
2349 post(conn, "/api/pleroma/admin/config", %{
2351 %{group: ":pleroma", key: ":key1", value: "value1"},
2352 %{group: ":pleroma", key: ":key2", value: "value2"},
2353 %{group: ":pleroma", key: ":key3", value: "value3"},
2354 %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
2355 %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
2356 %{group: ":not_real", key: ":anything", value: "value6"}
2360 assert Application.get_env(:pleroma, :key1) == "value1"
2361 assert Application.get_env(:pleroma, :key2) == "value2"
2362 assert Application.get_env(:pleroma, :key3) == nil
2363 assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
2364 assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
2365 assert Application.get_env(:not_real, :anything) == "value6"
2369 describe "GET /api/pleroma/admin/restart" do
2370 setup do: clear_config(:configurable_from_database, true)
2372 test "pleroma restarts", %{conn: conn} do
2374 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2375 end) =~ "pleroma restarted"
2377 refute Restarter.Pleroma.need_reboot?()
2381 test "need_reboot flag", %{conn: conn} do
2383 |> get("/api/pleroma/admin/need_reboot")
2384 |> json_response(200) == %{"need_reboot" => false}
2386 Restarter.Pleroma.need_reboot()
2389 |> get("/api/pleroma/admin/need_reboot")
2390 |> json_response(200) == %{"need_reboot" => true}
2392 on_exit(fn -> Restarter.Pleroma.refresh() end)
2395 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2397 user = insert(:user)
2399 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2400 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2401 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2403 insert(:note_activity, user: user, published: date1)
2404 insert(:note_activity, user: user, published: date2)
2405 insert(:note_activity, user: user, published: date3)
2410 test "renders user's statuses", %{conn: conn, user: user} do
2411 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2413 assert json_response(conn, 200) |> length() == 3
2416 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2417 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2419 assert json_response(conn, 200) |> length() == 2
2422 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2423 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
2425 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
2427 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2429 assert json_response(conn, 200) |> length() == 4
2432 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2433 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
2435 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
2437 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2439 assert json_response(conn, 200) |> length() == 5
2442 test "excludes reblogs by default", %{conn: conn, user: user} do
2443 other_user = insert(:user)
2444 {:ok, activity} = CommonAPI.post(user, %{status: "."})
2445 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
2447 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
2448 assert json_response(conn_res, 200) |> length() == 0
2451 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
2453 assert json_response(conn_res, 200) |> length() == 1
2457 describe "GET /api/pleroma/admin/moderation_log" do
2459 moderator = insert(:user, is_moderator: true)
2461 %{moderator: moderator}
2464 test "returns the log", %{conn: conn, admin: admin} do
2465 Repo.insert(%ModerationLog{
2469 "nickname" => admin.nickname,
2472 action: "relay_follow",
2473 target: "https://example.org/relay"
2475 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2478 Repo.insert(%ModerationLog{
2482 "nickname" => admin.nickname,
2485 action: "relay_unfollow",
2486 target: "https://example.org/relay"
2488 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2491 conn = get(conn, "/api/pleroma/admin/moderation_log")
2493 response = json_response(conn, 200)
2494 [first_entry, second_entry] = response["items"]
2496 assert response["total"] == 2
2497 assert first_entry["data"]["action"] == "relay_unfollow"
2499 assert first_entry["message"] ==
2500 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2502 assert second_entry["data"]["action"] == "relay_follow"
2504 assert second_entry["message"] ==
2505 "@#{admin.nickname} followed relay: https://example.org/relay"
2508 test "returns the log with pagination", %{conn: conn, admin: admin} do
2509 Repo.insert(%ModerationLog{
2513 "nickname" => admin.nickname,
2516 action: "relay_follow",
2517 target: "https://example.org/relay"
2519 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2522 Repo.insert(%ModerationLog{
2526 "nickname" => admin.nickname,
2529 action: "relay_unfollow",
2530 target: "https://example.org/relay"
2532 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2535 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
2537 response1 = json_response(conn1, 200)
2538 [first_entry] = response1["items"]
2540 assert response1["total"] == 2
2541 assert response1["items"] |> length() == 1
2542 assert first_entry["data"]["action"] == "relay_unfollow"
2544 assert first_entry["message"] ==
2545 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2547 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
2549 response2 = json_response(conn2, 200)
2550 [second_entry] = response2["items"]
2552 assert response2["total"] == 2
2553 assert response2["items"] |> length() == 1
2554 assert second_entry["data"]["action"] == "relay_follow"
2556 assert second_entry["message"] ==
2557 "@#{admin.nickname} followed relay: https://example.org/relay"
2560 test "filters log by date", %{conn: conn, admin: admin} do
2561 first_date = "2017-08-15T15:47:06Z"
2562 second_date = "2017-08-20T15:47:06Z"
2564 Repo.insert(%ModerationLog{
2568 "nickname" => admin.nickname,
2571 action: "relay_follow",
2572 target: "https://example.org/relay"
2574 inserted_at: NaiveDateTime.from_iso8601!(first_date)
2577 Repo.insert(%ModerationLog{
2581 "nickname" => admin.nickname,
2584 action: "relay_unfollow",
2585 target: "https://example.org/relay"
2587 inserted_at: NaiveDateTime.from_iso8601!(second_date)
2593 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
2596 response1 = json_response(conn1, 200)
2597 [first_entry] = response1["items"]
2599 assert response1["total"] == 1
2600 assert first_entry["data"]["action"] == "relay_unfollow"
2602 assert first_entry["message"] ==
2603 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2606 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
2607 Repo.insert(%ModerationLog{
2611 "nickname" => admin.nickname,
2614 action: "relay_follow",
2615 target: "https://example.org/relay"
2619 Repo.insert(%ModerationLog{
2622 "id" => moderator.id,
2623 "nickname" => moderator.nickname,
2626 action: "relay_unfollow",
2627 target: "https://example.org/relay"
2631 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
2633 response1 = json_response(conn1, 200)
2634 [first_entry] = response1["items"]
2636 assert response1["total"] == 1
2637 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
2640 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
2641 ModerationLog.insert_log(%{
2643 action: "relay_follow",
2644 target: "https://example.org/relay"
2647 ModerationLog.insert_log(%{
2649 action: "relay_unfollow",
2650 target: "https://example.org/relay"
2653 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
2655 response1 = json_response(conn1, 200)
2656 [first_entry] = response1["items"]
2658 assert response1["total"] == 1
2660 assert get_in(first_entry, ["data", "message"]) ==
2661 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
2665 describe "GET /users/:nickname/credentials" do
2666 test "gets the user credentials", %{conn: conn} do
2667 user = insert(:user)
2668 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
2670 response = assert json_response(conn, 200)
2671 assert response["email"] == user.email
2674 test "returns 403 if requested by a non-admin" do
2675 user = insert(:user)
2679 |> assign(:user, user)
2680 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
2682 assert json_response(conn, :forbidden)
2686 describe "PATCH /users/:nickname/credentials" do
2688 user = insert(:user)
2692 test "changes password and email", %{conn: conn, admin: admin, user: user} do
2693 assert user.password_reset_pending == false
2696 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2697 "password" => "new_password",
2698 "email" => "new_email@example.com",
2699 "name" => "new_name"
2702 assert json_response(conn, 200) == %{"status" => "success"}
2704 ObanHelpers.perform_all()
2706 updated_user = User.get_by_id(user.id)
2708 assert updated_user.email == "new_email@example.com"
2709 assert updated_user.name == "new_name"
2710 assert updated_user.password_hash != user.password_hash
2711 assert updated_user.password_reset_pending == true
2713 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
2715 assert ModerationLog.get_log_entry_message(log_entry1) ==
2716 "@#{admin.nickname} updated users: @#{user.nickname}"
2718 assert ModerationLog.get_log_entry_message(log_entry2) ==
2719 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
2722 test "returns 403 if requested by a non-admin", %{user: user} do
2725 |> assign(:user, user)
2726 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2727 "password" => "new_password",
2728 "email" => "new_email@example.com",
2729 "name" => "new_name"
2732 assert json_response(conn, :forbidden)
2735 test "changes actor type from permitted list", %{conn: conn, user: user} do
2736 assert user.actor_type == "Person"
2738 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2739 "actor_type" => "Service"
2741 |> json_response(200) == %{"status" => "success"}
2743 updated_user = User.get_by_id(user.id)
2745 assert updated_user.actor_type == "Service"
2747 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
2748 "actor_type" => "Application"
2750 |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
2753 test "update non existing user", %{conn: conn} do
2754 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
2755 "password" => "new_password"
2757 |> json_response(200) == %{"error" => "Unable to update user."}
2761 describe "PATCH /users/:nickname/force_password_reset" do
2762 test "sets password_reset_pending to true", %{conn: conn} do
2763 user = insert(:user)
2764 assert user.password_reset_pending == false
2767 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
2769 assert json_response(conn, 204) == ""
2771 ObanHelpers.perform_all()
2773 assert User.get_by_id(user.id).password_reset_pending == true
2777 describe "relays" do
2778 test "POST /relay", %{conn: conn, admin: admin} do
2780 post(conn, "/api/pleroma/admin/relay", %{
2781 relay_url: "http://mastodon.example.org/users/admin"
2784 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2786 log_entry = Repo.one(ModerationLog)
2788 assert ModerationLog.get_log_entry_message(log_entry) ==
2789 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2792 test "GET /relay", %{conn: conn} do
2793 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
2795 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
2796 |> Enum.each(fn ap_id ->
2797 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
2798 User.follow(relay_user, user)
2801 conn = get(conn, "/api/pleroma/admin/relay")
2803 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
2806 test "DELETE /relay", %{conn: conn, admin: admin} do
2807 post(conn, "/api/pleroma/admin/relay", %{
2808 relay_url: "http://mastodon.example.org/users/admin"
2812 delete(conn, "/api/pleroma/admin/relay", %{
2813 relay_url: "http://mastodon.example.org/users/admin"
2816 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2818 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
2820 assert ModerationLog.get_log_entry_message(log_entry_one) ==
2821 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2823 assert ModerationLog.get_log_entry_message(log_entry_two) ==
2824 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
2828 describe "instances" do
2829 test "GET /instances/:instance/statuses", %{conn: conn} do
2830 user = insert(:user, local: false, nickname: "archaeme@archae.me")
2831 user2 = insert(:user, local: false, nickname: "test@test.com")
2832 insert_pair(:note_activity, user: user)
2833 activity = insert(:note_activity, user: user2)
2835 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
2837 response = json_response(ret_conn, 200)
2839 assert length(response) == 2
2841 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
2843 response = json_response(ret_conn, 200)
2845 assert length(response) == 1
2847 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
2849 response = json_response(ret_conn, 200)
2851 assert Enum.empty?(response)
2853 CommonAPI.repeat(activity.id, user)
2855 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
2856 response = json_response(ret_conn, 200)
2857 assert length(response) == 2
2859 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
2860 response = json_response(ret_conn, 200)
2861 assert length(response) == 3
2865 describe "PATCH /confirm_email" do
2866 test "it confirms emails of two users", %{conn: conn, admin: admin} do
2867 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2869 assert first_user.confirmation_pending == true
2870 assert second_user.confirmation_pending == true
2873 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
2875 first_user.nickname,
2876 second_user.nickname
2880 assert ret_conn.status == 200
2882 assert first_user.confirmation_pending == true
2883 assert second_user.confirmation_pending == true
2885 log_entry = Repo.one(ModerationLog)
2887 assert ModerationLog.get_log_entry_message(log_entry) ==
2888 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
2889 second_user.nickname
2894 describe "PATCH /resend_confirmation_email" do
2895 test "it resend emails for two users", %{conn: conn, admin: admin} do
2896 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2899 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
2901 first_user.nickname,
2902 second_user.nickname
2906 assert ret_conn.status == 200
2908 log_entry = Repo.one(ModerationLog)
2910 assert ModerationLog.get_log_entry_message(log_entry) ==
2911 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
2912 second_user.nickname
2917 describe "GET /api/pleroma/admin/config/descriptions" do
2918 test "structure", %{conn: conn} do
2919 admin = insert(:user, is_admin: true)
2922 assign(conn, :user, admin)
2923 |> get("/api/pleroma/admin/config/descriptions")
2925 assert [child | _others] = json_response(conn, 200)
2927 assert child["children"]
2929 assert String.starts_with?(child["group"], ":")
2930 assert child["description"]
2933 test "filters by database configuration whitelist", %{conn: conn} do
2934 clear_config(:database_config_whitelist, [
2935 {:pleroma, :instance},
2936 {:pleroma, :activitypub},
2937 {:pleroma, Pleroma.Upload},
2941 admin = insert(:user, is_admin: true)
2944 assign(conn, :user, admin)
2945 |> get("/api/pleroma/admin/config/descriptions")
2947 children = json_response(conn, 200)
2949 assert length(children) == 4
2951 assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
2953 instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
2954 assert instance["children"]
2956 activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
2957 assert activitypub["children"]
2959 web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
2960 assert web_endpoint["children"]
2962 esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
2963 assert esshd["children"]
2967 describe "/api/pleroma/admin/stats" do
2968 test "status visibility count", %{conn: conn} do
2969 admin = insert(:user, is_admin: true)
2970 user = insert(:user)
2971 CommonAPI.post(user, %{visibility: "public", status: "hey"})
2972 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
2973 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
2977 |> assign(:user, admin)
2978 |> get("/api/pleroma/admin/stats")
2979 |> json_response(200)
2981 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
2982 response["status_visibility"]
2987 # Needed for testing
2988 defmodule Pleroma.Web.Endpoint.NotReal do
2991 defmodule Pleroma.Captcha.NotReal do