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.ReportNote
21 alias Pleroma.Tests.ObanHelpers
23 alias Pleroma.UserInviteToken
25 alias Pleroma.Web.ActivityPub.Relay
26 alias Pleroma.Web.CommonAPI
27 alias Pleroma.Web.MediaProxy
30 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 admin = insert(:user, is_admin: true)
37 token = insert(:oauth_admin_token, user: admin)
41 |> assign(:user, admin)
42 |> assign(:token, token)
44 {:ok, %{admin: admin, token: token, conn: conn}}
47 describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
48 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
50 test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
53 url = "/api/pleroma/admin/users/#{user.nickname}"
55 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
56 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
57 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
59 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
60 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
63 for good_token <- [good_token1, good_token2, good_token3] do
66 |> assign(:user, admin)
67 |> assign(:token, good_token)
70 assert json_response(conn, 200)
73 for good_token <- [good_token1, good_token2, good_token3] do
77 |> assign(:token, good_token)
80 assert json_response(conn, :forbidden)
83 for bad_token <- [bad_token1, bad_token2, bad_token3] do
86 |> assign(:user, admin)
87 |> assign(:token, bad_token)
90 assert json_response(conn, :forbidden)
95 describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
96 setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
98 test "GET /api/pleroma/admin/users/:nickname requires " <>
99 "read:accounts or admin:read:accounts or broader scope",
102 url = "/api/pleroma/admin/users/#{user.nickname}"
104 good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
105 good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
106 good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
107 good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
108 good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
110 good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
112 bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
113 bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
116 for good_token <- good_tokens do
119 |> assign(:user, admin)
120 |> assign(:token, good_token)
123 assert json_response(conn, 200)
126 for good_token <- good_tokens do
129 |> assign(:user, nil)
130 |> assign(:token, good_token)
133 assert json_response(conn, :forbidden)
136 for bad_token <- [bad_token1, bad_token2, bad_token3] do
139 |> assign(:user, admin)
140 |> assign(:token, bad_token)
143 assert json_response(conn, :forbidden)
148 describe "DELETE /api/pleroma/admin/users" do
149 test "single user", %{admin: admin, conn: conn} do
151 clear_config([:instance, :federating], true)
153 with_mock Pleroma.Web.Federator,
154 publish: fn _ -> nil end do
157 |> put_req_header("accept", "application/json")
158 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
160 ObanHelpers.perform_all()
162 assert User.get_by_nickname(user.nickname).deactivated
164 log_entry = Repo.one(ModerationLog)
166 assert ModerationLog.get_log_entry_message(log_entry) ==
167 "@#{admin.nickname} deleted users: @#{user.nickname}"
169 assert json_response(conn, 200) == [user.nickname]
171 assert called(Pleroma.Web.Federator.publish(:_))
175 test "multiple users", %{admin: admin, conn: conn} do
176 user_one = insert(:user)
177 user_two = insert(:user)
181 |> put_req_header("accept", "application/json")
182 |> delete("/api/pleroma/admin/users", %{
183 nicknames: [user_one.nickname, user_two.nickname]
186 log_entry = Repo.one(ModerationLog)
188 assert ModerationLog.get_log_entry_message(log_entry) ==
189 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
191 response = json_response(conn, 200)
192 assert response -- [user_one.nickname, user_two.nickname] == []
196 describe "/api/pleroma/admin/users" do
197 test "Create", %{conn: conn} do
200 |> put_req_header("accept", "application/json")
201 |> post("/api/pleroma/admin/users", %{
204 "nickname" => "lain",
205 "email" => "lain@example.org",
209 "nickname" => "lain2",
210 "email" => "lain2@example.org",
216 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
217 assert response == ["success", "success"]
219 log_entry = Repo.one(ModerationLog)
221 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
224 test "Cannot create user with existing email", %{conn: conn} do
229 |> put_req_header("accept", "application/json")
230 |> post("/api/pleroma/admin/users", %{
233 "nickname" => "lain",
234 "email" => user.email,
240 assert json_response(conn, 409) == [
244 "email" => user.email,
247 "error" => "email has already been taken",
253 test "Cannot create user with existing nickname", %{conn: conn} do
258 |> put_req_header("accept", "application/json")
259 |> post("/api/pleroma/admin/users", %{
262 "nickname" => user.nickname,
263 "email" => "someuser@plerama.social",
269 assert json_response(conn, 409) == [
273 "email" => "someuser@plerama.social",
274 "nickname" => user.nickname
276 "error" => "nickname has already been taken",
282 test "Multiple user creation works in transaction", %{conn: conn} do
287 |> put_req_header("accept", "application/json")
288 |> post("/api/pleroma/admin/users", %{
291 "nickname" => "newuser",
292 "email" => "newuser@pleroma.social",
296 "nickname" => "lain",
297 "email" => user.email,
303 assert json_response(conn, 409) == [
307 "email" => user.email,
310 "error" => "email has already been taken",
316 "email" => "newuser@pleroma.social",
317 "nickname" => "newuser"
324 assert User.get_by_nickname("newuser") === nil
328 describe "/api/pleroma/admin/users/:nickname" do
329 test "Show", %{conn: conn} do
332 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
335 "deactivated" => false,
336 "id" => to_string(user.id),
338 "nickname" => user.nickname,
339 "roles" => %{"admin" => false, "moderator" => false},
341 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
342 "display_name" => HTML.strip_tags(user.name || user.nickname),
343 "confirmation_pending" => false
346 assert expected == json_response(conn, 200)
349 test "when the user doesn't exist", %{conn: conn} do
352 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
354 assert %{"error" => "Not found"} == json_response(conn, 404)
358 describe "/api/pleroma/admin/users/follow" do
359 test "allows to force-follow another user", %{admin: admin, conn: conn} do
361 follower = insert(:user)
364 |> put_req_header("accept", "application/json")
365 |> post("/api/pleroma/admin/users/follow", %{
366 "follower" => follower.nickname,
367 "followed" => user.nickname
370 user = User.get_cached_by_id(user.id)
371 follower = User.get_cached_by_id(follower.id)
373 assert User.following?(follower, user)
375 log_entry = Repo.one(ModerationLog)
377 assert ModerationLog.get_log_entry_message(log_entry) ==
378 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
382 describe "/api/pleroma/admin/users/unfollow" do
383 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
385 follower = insert(:user)
387 User.follow(follower, user)
390 |> put_req_header("accept", "application/json")
391 |> post("/api/pleroma/admin/users/unfollow", %{
392 "follower" => follower.nickname,
393 "followed" => user.nickname
396 user = User.get_cached_by_id(user.id)
397 follower = User.get_cached_by_id(follower.id)
399 refute User.following?(follower, user)
401 log_entry = Repo.one(ModerationLog)
403 assert ModerationLog.get_log_entry_message(log_entry) ==
404 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
408 describe "PUT /api/pleroma/admin/users/tag" do
409 setup %{conn: conn} do
410 user1 = insert(:user, %{tags: ["x"]})
411 user2 = insert(:user, %{tags: ["y"]})
412 user3 = insert(:user, %{tags: ["unchanged"]})
416 |> put_req_header("accept", "application/json")
418 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
419 "#{user2.nickname}&tags[]=foo&tags[]=bar"
422 %{conn: conn, user1: user1, user2: user2, user3: user3}
425 test "it appends specified tags to users with specified nicknames", %{
431 assert json_response(conn, :no_content)
432 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
433 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
435 log_entry = Repo.one(ModerationLog)
438 [user1.nickname, user2.nickname]
439 |> Enum.map(&"@#{&1}")
442 tags = ["foo", "bar"] |> Enum.join(", ")
444 assert ModerationLog.get_log_entry_message(log_entry) ==
445 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
448 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
449 assert json_response(conn, :no_content)
450 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
454 describe "DELETE /api/pleroma/admin/users/tag" do
455 setup %{conn: conn} do
456 user1 = insert(:user, %{tags: ["x"]})
457 user2 = insert(:user, %{tags: ["y", "z"]})
458 user3 = insert(:user, %{tags: ["unchanged"]})
462 |> put_req_header("accept", "application/json")
464 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
465 "#{user2.nickname}&tags[]=x&tags[]=z"
468 %{conn: conn, user1: user1, user2: user2, user3: user3}
471 test "it removes specified tags from users with specified nicknames", %{
477 assert json_response(conn, :no_content)
478 assert User.get_cached_by_id(user1.id).tags == []
479 assert User.get_cached_by_id(user2.id).tags == ["y"]
481 log_entry = Repo.one(ModerationLog)
484 [user1.nickname, user2.nickname]
485 |> Enum.map(&"@#{&1}")
488 tags = ["x", "z"] |> Enum.join(", ")
490 assert ModerationLog.get_log_entry_message(log_entry) ==
491 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
494 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
495 assert json_response(conn, :no_content)
496 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
500 describe "/api/pleroma/admin/users/:nickname/permission_group" do
501 test "GET is giving user_info", %{admin: admin, conn: conn} do
504 |> put_req_header("accept", "application/json")
505 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
507 assert json_response(conn, 200) == %{
509 "is_moderator" => false
513 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
518 |> put_req_header("accept", "application/json")
519 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
521 assert json_response(conn, 200) == %{
525 log_entry = Repo.one(ModerationLog)
527 assert ModerationLog.get_log_entry_message(log_entry) ==
528 "@#{admin.nickname} made @#{user.nickname} admin"
531 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
532 user_one = insert(:user)
533 user_two = insert(:user)
537 |> put_req_header("accept", "application/json")
538 |> post("/api/pleroma/admin/users/permission_group/admin", %{
539 nicknames: [user_one.nickname, user_two.nickname]
542 assert json_response(conn, 200) == %{"is_admin" => true}
544 log_entry = Repo.one(ModerationLog)
546 assert ModerationLog.get_log_entry_message(log_entry) ==
547 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
550 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
551 user = insert(:user, is_admin: true)
555 |> put_req_header("accept", "application/json")
556 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
558 assert json_response(conn, 200) == %{"is_admin" => false}
560 log_entry = Repo.one(ModerationLog)
562 assert ModerationLog.get_log_entry_message(log_entry) ==
563 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
566 test "/:right DELETE, can remove from a permission group (multiple)", %{
570 user_one = insert(:user, is_admin: true)
571 user_two = insert(:user, is_admin: true)
575 |> put_req_header("accept", "application/json")
576 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
577 nicknames: [user_one.nickname, user_two.nickname]
580 assert json_response(conn, 200) == %{"is_admin" => false}
582 log_entry = Repo.one(ModerationLog)
584 assert ModerationLog.get_log_entry_message(log_entry) ==
585 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
591 describe "POST /api/pleroma/admin/email_invite, with valid config" do
592 setup do: clear_config([:instance, :registrations_open], false)
593 setup do: clear_config([:instance, :invites_enabled], true)
595 test "sends invitation and returns 204", %{admin: admin, conn: conn} do
596 recipient_email = "foo@bar.com"
597 recipient_name = "J. D."
602 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
605 assert json_response(conn, :no_content)
607 token_record = List.last(Repo.all(Pleroma.UserInviteToken))
609 refute token_record.used
611 notify_email = Config.get([:instance, :notify_email])
612 instance_name = Config.get([:instance, :name])
615 Pleroma.Emails.UserEmail.user_invitation_email(
622 Swoosh.TestAssertions.assert_email_sent(
623 from: {instance_name, notify_email},
624 to: {recipient_name, recipient_email},
625 html_body: email.html_body
629 test "it returns 403 if requested by a non-admin" do
630 non_admin_user = insert(:user)
631 token = insert(:oauth_token, user: non_admin_user)
635 |> assign(:user, non_admin_user)
636 |> assign(:token, token)
637 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
639 assert json_response(conn, :forbidden)
642 test "email with +", %{conn: conn, admin: admin} do
643 recipient_email = "foo+bar@baz.com"
646 |> put_req_header("content-type", "application/json;charset=utf-8")
647 |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
648 |> json_response(:no_content)
651 Pleroma.UserInviteToken
656 refute token_record.used
658 notify_email = Config.get([:instance, :notify_email])
659 instance_name = Config.get([:instance, :name])
662 Pleroma.Emails.UserEmail.user_invitation_email(
668 Swoosh.TestAssertions.assert_email_sent(
669 from: {instance_name, notify_email},
671 html_body: email.html_body
676 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
677 setup do: clear_config([:instance, :registrations_open])
678 setup do: clear_config([:instance, :invites_enabled])
680 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
681 Config.put([:instance, :registrations_open], false)
682 Config.put([:instance, :invites_enabled], false)
684 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
686 assert json_response(conn, :bad_request) ==
689 "To send invites you need to set the `invites_enabled` option to true."
693 test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
694 Config.put([:instance, :registrations_open], true)
695 Config.put([:instance, :invites_enabled], true)
697 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
699 assert json_response(conn, :bad_request) ==
702 "To send invites you need to set the `registrations_open` option to false."
707 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
712 |> put_req_header("accept", "application/json")
713 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
715 resp = json_response(conn, 200)
717 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
720 describe "GET /api/pleroma/admin/users" do
721 test "renders users array for the first page", %{conn: conn, admin: admin} do
722 user = insert(:user, local: false, tags: ["foo", "bar"])
723 conn = get(conn, "/api/pleroma/admin/users?page=1")
728 "deactivated" => admin.deactivated,
730 "nickname" => admin.nickname,
731 "roles" => %{"admin" => true, "moderator" => false},
734 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
735 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
736 "confirmation_pending" => false
739 "deactivated" => user.deactivated,
741 "nickname" => user.nickname,
742 "roles" => %{"admin" => false, "moderator" => false},
744 "tags" => ["foo", "bar"],
745 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
746 "display_name" => HTML.strip_tags(user.name || user.nickname),
747 "confirmation_pending" => false
750 |> Enum.sort_by(& &1["nickname"])
752 assert json_response(conn, 200) == %{
759 test "pagination works correctly with service users", %{conn: conn} do
760 service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
762 insert_list(25, :user)
764 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
766 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
767 |> json_response(200)
769 assert Enum.count(users1) == 10
770 assert service1 not in users1
772 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
774 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
775 |> json_response(200)
777 assert Enum.count(users2) == 10
778 assert service1 not in users2
780 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
782 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
783 |> json_response(200)
785 assert Enum.count(users3) == 6
786 assert service1 not in users3
789 test "renders empty array for the second page", %{conn: conn} do
792 conn = get(conn, "/api/pleroma/admin/users?page=2")
794 assert json_response(conn, 200) == %{
801 test "regular search", %{conn: conn} do
802 user = insert(:user, nickname: "bob")
804 conn = get(conn, "/api/pleroma/admin/users?query=bo")
806 assert json_response(conn, 200) == %{
811 "deactivated" => user.deactivated,
813 "nickname" => user.nickname,
814 "roles" => %{"admin" => false, "moderator" => false},
817 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
818 "display_name" => HTML.strip_tags(user.name || user.nickname),
819 "confirmation_pending" => false
825 test "search by domain", %{conn: conn} do
826 user = insert(:user, nickname: "nickname@domain.com")
829 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
831 assert json_response(conn, 200) == %{
836 "deactivated" => user.deactivated,
838 "nickname" => user.nickname,
839 "roles" => %{"admin" => false, "moderator" => false},
842 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
843 "display_name" => HTML.strip_tags(user.name || user.nickname),
844 "confirmation_pending" => false
850 test "search by full nickname", %{conn: conn} do
851 user = insert(:user, nickname: "nickname@domain.com")
854 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
856 assert json_response(conn, 200) == %{
861 "deactivated" => user.deactivated,
863 "nickname" => user.nickname,
864 "roles" => %{"admin" => false, "moderator" => false},
867 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
868 "display_name" => HTML.strip_tags(user.name || user.nickname),
869 "confirmation_pending" => false
875 test "search by display name", %{conn: conn} do
876 user = insert(:user, name: "Display name")
879 conn = get(conn, "/api/pleroma/admin/users?name=display")
881 assert json_response(conn, 200) == %{
886 "deactivated" => user.deactivated,
888 "nickname" => user.nickname,
889 "roles" => %{"admin" => false, "moderator" => false},
892 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
893 "display_name" => HTML.strip_tags(user.name || user.nickname),
894 "confirmation_pending" => false
900 test "search by email", %{conn: conn} do
901 user = insert(:user, email: "email@example.com")
904 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
906 assert json_response(conn, 200) == %{
911 "deactivated" => user.deactivated,
913 "nickname" => user.nickname,
914 "roles" => %{"admin" => false, "moderator" => false},
917 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
918 "display_name" => HTML.strip_tags(user.name || user.nickname),
919 "confirmation_pending" => false
925 test "regular search with page size", %{conn: conn} do
926 user = insert(:user, nickname: "aalice")
927 user2 = insert(:user, nickname: "alice")
929 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
931 assert json_response(conn1, 200) == %{
936 "deactivated" => user.deactivated,
938 "nickname" => user.nickname,
939 "roles" => %{"admin" => false, "moderator" => false},
942 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
943 "display_name" => HTML.strip_tags(user.name || user.nickname),
944 "confirmation_pending" => false
949 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
951 assert json_response(conn2, 200) == %{
956 "deactivated" => user2.deactivated,
958 "nickname" => user2.nickname,
959 "roles" => %{"admin" => false, "moderator" => false},
962 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
963 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
964 "confirmation_pending" => false
970 test "only local users" do
971 admin = insert(:user, is_admin: true, nickname: "john")
972 token = insert(:oauth_admin_token, user: admin)
973 user = insert(:user, nickname: "bob")
975 insert(:user, nickname: "bobb", local: false)
979 |> assign(:user, admin)
980 |> assign(:token, token)
981 |> get("/api/pleroma/admin/users?query=bo&filters=local")
983 assert json_response(conn, 200) == %{
988 "deactivated" => user.deactivated,
990 "nickname" => user.nickname,
991 "roles" => %{"admin" => false, "moderator" => false},
994 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
995 "display_name" => HTML.strip_tags(user.name || user.nickname),
996 "confirmation_pending" => false
1002 test "only local users with no query", %{conn: conn, admin: old_admin} do
1003 admin = insert(:user, is_admin: true, nickname: "john")
1004 user = insert(:user, nickname: "bob")
1006 insert(:user, nickname: "bobb", local: false)
1008 conn = get(conn, "/api/pleroma/admin/users?filters=local")
1013 "deactivated" => user.deactivated,
1015 "nickname" => user.nickname,
1016 "roles" => %{"admin" => false, "moderator" => false},
1019 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1020 "display_name" => HTML.strip_tags(user.name || user.nickname),
1021 "confirmation_pending" => false
1024 "deactivated" => admin.deactivated,
1026 "nickname" => admin.nickname,
1027 "roles" => %{"admin" => true, "moderator" => false},
1030 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1031 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1032 "confirmation_pending" => false
1035 "deactivated" => false,
1036 "id" => old_admin.id,
1038 "nickname" => old_admin.nickname,
1039 "roles" => %{"admin" => true, "moderator" => false},
1041 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
1042 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
1043 "confirmation_pending" => false
1046 |> Enum.sort_by(& &1["nickname"])
1048 assert json_response(conn, 200) == %{
1055 test "load only admins", %{conn: conn, admin: admin} do
1056 second_admin = insert(:user, is_admin: true)
1060 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
1065 "deactivated" => false,
1067 "nickname" => admin.nickname,
1068 "roles" => %{"admin" => true, "moderator" => false},
1069 "local" => admin.local,
1071 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1072 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1073 "confirmation_pending" => false
1076 "deactivated" => false,
1077 "id" => second_admin.id,
1078 "nickname" => second_admin.nickname,
1079 "roles" => %{"admin" => true, "moderator" => false},
1080 "local" => second_admin.local,
1082 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1083 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1084 "confirmation_pending" => false
1087 |> Enum.sort_by(& &1["nickname"])
1089 assert json_response(conn, 200) == %{
1096 test "load only moderators", %{conn: conn} do
1097 moderator = insert(:user, is_moderator: true)
1101 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1103 assert json_response(conn, 200) == %{
1108 "deactivated" => false,
1109 "id" => moderator.id,
1110 "nickname" => moderator.nickname,
1111 "roles" => %{"admin" => false, "moderator" => true},
1112 "local" => moderator.local,
1114 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1115 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1116 "confirmation_pending" => false
1122 test "load users with tags list", %{conn: conn} do
1123 user1 = insert(:user, tags: ["first"])
1124 user2 = insert(:user, tags: ["second"])
1128 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1133 "deactivated" => false,
1135 "nickname" => user1.nickname,
1136 "roles" => %{"admin" => false, "moderator" => false},
1137 "local" => user1.local,
1138 "tags" => ["first"],
1139 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1140 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1141 "confirmation_pending" => false
1144 "deactivated" => false,
1146 "nickname" => user2.nickname,
1147 "roles" => %{"admin" => false, "moderator" => false},
1148 "local" => user2.local,
1149 "tags" => ["second"],
1150 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1151 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1152 "confirmation_pending" => false
1155 |> Enum.sort_by(& &1["nickname"])
1157 assert json_response(conn, 200) == %{
1164 test "it works with multiple filters" do
1165 admin = insert(:user, nickname: "john", is_admin: true)
1166 token = insert(:oauth_admin_token, user: admin)
1167 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1169 insert(:user, nickname: "ken", local: true, deactivated: true)
1170 insert(:user, nickname: "bobb", local: false, deactivated: false)
1174 |> assign(:user, admin)
1175 |> assign(:token, token)
1176 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1178 assert json_response(conn, 200) == %{
1183 "deactivated" => user.deactivated,
1185 "nickname" => user.nickname,
1186 "roles" => %{"admin" => false, "moderator" => false},
1187 "local" => user.local,
1189 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1190 "display_name" => HTML.strip_tags(user.name || user.nickname),
1191 "confirmation_pending" => false
1197 test "it omits relay user", %{admin: admin, conn: conn} do
1198 assert %User{} = Relay.get_actor()
1200 conn = get(conn, "/api/pleroma/admin/users")
1202 assert json_response(conn, 200) == %{
1207 "deactivated" => admin.deactivated,
1209 "nickname" => admin.nickname,
1210 "roles" => %{"admin" => true, "moderator" => false},
1213 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1214 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1215 "confirmation_pending" => false
1222 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1223 user_one = insert(:user, deactivated: true)
1224 user_two = insert(:user, deactivated: true)
1229 "/api/pleroma/admin/users/activate",
1230 %{nicknames: [user_one.nickname, user_two.nickname]}
1233 response = json_response(conn, 200)
1234 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1236 log_entry = Repo.one(ModerationLog)
1238 assert ModerationLog.get_log_entry_message(log_entry) ==
1239 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1242 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1243 user_one = insert(:user, deactivated: false)
1244 user_two = insert(:user, deactivated: false)
1249 "/api/pleroma/admin/users/deactivate",
1250 %{nicknames: [user_one.nickname, user_two.nickname]}
1253 response = json_response(conn, 200)
1254 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1256 log_entry = Repo.one(ModerationLog)
1258 assert ModerationLog.get_log_entry_message(log_entry) ==
1259 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1262 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1263 user = insert(:user)
1265 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1267 assert json_response(conn, 200) ==
1269 "deactivated" => !user.deactivated,
1271 "nickname" => user.nickname,
1272 "roles" => %{"admin" => false, "moderator" => false},
1275 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1276 "display_name" => HTML.strip_tags(user.name || user.nickname),
1277 "confirmation_pending" => false
1280 log_entry = Repo.one(ModerationLog)
1282 assert ModerationLog.get_log_entry_message(log_entry) ==
1283 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1286 describe "PUT disable_mfa" do
1287 test "returns 200 and disable 2fa", %{conn: conn} do
1290 multi_factor_authentication_settings: %MFA.Settings{
1292 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1298 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1299 |> json_response(200)
1301 assert response == user.nickname
1302 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1304 refute mfa_settings.enabled
1305 refute mfa_settings.totp.confirmed
1308 test "returns 404 if user not found", %{conn: conn} do
1311 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1312 |> json_response(404)
1314 assert response == %{"error" => "Not found"}
1318 describe "POST /api/pleroma/admin/users/invite_token" do
1319 test "without options", %{conn: conn} do
1320 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1322 invite_json = json_response(conn, 200)
1323 invite = UserInviteToken.find_by_token!(invite_json["token"])
1325 refute invite.expires_at
1326 refute invite.max_use
1327 assert invite.invite_type == "one_time"
1330 test "with expires_at", %{conn: conn} do
1332 post(conn, "/api/pleroma/admin/users/invite_token", %{
1333 "expires_at" => Date.to_string(Date.utc_today())
1336 invite_json = json_response(conn, 200)
1337 invite = UserInviteToken.find_by_token!(invite_json["token"])
1340 assert invite.expires_at == Date.utc_today()
1341 refute invite.max_use
1342 assert invite.invite_type == "date_limited"
1345 test "with max_use", %{conn: conn} do
1346 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1348 invite_json = json_response(conn, 200)
1349 invite = UserInviteToken.find_by_token!(invite_json["token"])
1351 refute invite.expires_at
1352 assert invite.max_use == 150
1353 assert invite.invite_type == "reusable"
1356 test "with max use and expires_at", %{conn: conn} do
1358 post(conn, "/api/pleroma/admin/users/invite_token", %{
1360 "expires_at" => Date.to_string(Date.utc_today())
1363 invite_json = json_response(conn, 200)
1364 invite = UserInviteToken.find_by_token!(invite_json["token"])
1366 assert invite.expires_at == Date.utc_today()
1367 assert invite.max_use == 150
1368 assert invite.invite_type == "reusable_date_limited"
1372 describe "GET /api/pleroma/admin/users/invites" do
1373 test "no invites", %{conn: conn} do
1374 conn = get(conn, "/api/pleroma/admin/users/invites")
1376 assert json_response(conn, 200) == %{"invites" => []}
1379 test "with invite", %{conn: conn} do
1380 {:ok, invite} = UserInviteToken.create_invite()
1382 conn = get(conn, "/api/pleroma/admin/users/invites")
1384 assert json_response(conn, 200) == %{
1387 "expires_at" => nil,
1389 "invite_type" => "one_time",
1391 "token" => invite.token,
1400 describe "POST /api/pleroma/admin/users/revoke_invite" do
1401 test "with token", %{conn: conn} do
1402 {:ok, invite} = UserInviteToken.create_invite()
1404 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1406 assert json_response(conn, 200) == %{
1407 "expires_at" => nil,
1409 "invite_type" => "one_time",
1411 "token" => invite.token,
1417 test "with invalid token", %{conn: conn} do
1418 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1420 assert json_response(conn, :not_found) == %{"error" => "Not found"}
1424 describe "GET /api/pleroma/admin/reports/:id" do
1425 test "returns report by its id", %{conn: conn} do
1426 [reporter, target_user] = insert_pair(:user)
1427 activity = insert(:note_activity, user: target_user)
1429 {:ok, %{id: report_id}} =
1430 CommonAPI.report(reporter, %{
1431 account_id: target_user.id,
1432 comment: "I feel offended",
1433 status_ids: [activity.id]
1438 |> get("/api/pleroma/admin/reports/#{report_id}")
1439 |> json_response(:ok)
1441 assert response["id"] == report_id
1444 test "returns 404 when report id is invalid", %{conn: conn} do
1445 conn = get(conn, "/api/pleroma/admin/reports/test")
1447 assert json_response(conn, :not_found) == %{"error" => "Not found"}
1451 describe "PATCH /api/pleroma/admin/reports" do
1453 [reporter, target_user] = insert_pair(:user)
1454 activity = insert(:note_activity, user: target_user)
1456 {:ok, %{id: report_id}} =
1457 CommonAPI.report(reporter, %{
1458 account_id: target_user.id,
1459 comment: "I feel offended",
1460 status_ids: [activity.id]
1463 {:ok, %{id: second_report_id}} =
1464 CommonAPI.report(reporter, %{
1465 account_id: target_user.id,
1466 comment: "I feel very offended",
1467 status_ids: [activity.id]
1472 second_report_id: second_report_id
1476 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1477 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1478 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1482 |> assign(:token, read_token)
1483 |> patch("/api/pleroma/admin/reports", %{
1484 "reports" => [%{"state" => "resolved", "id" => id}]
1486 |> json_response(403)
1488 assert response == %{
1489 "error" => "Insufficient permissions: admin:write:reports."
1493 |> assign(:token, write_token)
1494 |> patch("/api/pleroma/admin/reports", %{
1495 "reports" => [%{"state" => "resolved", "id" => id}]
1497 |> json_response(:no_content)
1500 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1502 |> patch("/api/pleroma/admin/reports", %{
1504 %{"state" => "resolved", "id" => id}
1507 |> json_response(:no_content)
1509 activity = Activity.get_by_id(id)
1510 assert activity.data["state"] == "resolved"
1512 log_entry = Repo.one(ModerationLog)
1514 assert ModerationLog.get_log_entry_message(log_entry) ==
1515 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1518 test "closes report", %{conn: conn, id: id, admin: admin} do
1520 |> patch("/api/pleroma/admin/reports", %{
1522 %{"state" => "closed", "id" => id}
1525 |> json_response(:no_content)
1527 activity = Activity.get_by_id(id)
1528 assert activity.data["state"] == "closed"
1530 log_entry = Repo.one(ModerationLog)
1532 assert ModerationLog.get_log_entry_message(log_entry) ==
1533 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1536 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1539 |> patch("/api/pleroma/admin/reports", %{
1541 %{"state" => "test", "id" => id}
1545 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1548 test "returns 404 when report is not exist", %{conn: conn} do
1551 |> patch("/api/pleroma/admin/reports", %{
1553 %{"state" => "closed", "id" => "test"}
1557 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1560 test "updates state of multiple reports", %{
1564 second_report_id: second_report_id
1567 |> patch("/api/pleroma/admin/reports", %{
1569 %{"state" => "resolved", "id" => id},
1570 %{"state" => "closed", "id" => second_report_id}
1573 |> json_response(:no_content)
1575 activity = Activity.get_by_id(id)
1576 second_activity = Activity.get_by_id(second_report_id)
1577 assert activity.data["state"] == "resolved"
1578 assert second_activity.data["state"] == "closed"
1580 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1582 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1583 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1585 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1586 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1590 describe "GET /api/pleroma/admin/reports" do
1591 test "returns empty response when no reports created", %{conn: conn} do
1594 |> get("/api/pleroma/admin/reports")
1595 |> json_response(:ok)
1597 assert Enum.empty?(response["reports"])
1598 assert response["total"] == 0
1601 test "returns reports", %{conn: conn} do
1602 [reporter, target_user] = insert_pair(:user)
1603 activity = insert(:note_activity, user: target_user)
1605 {:ok, %{id: report_id}} =
1606 CommonAPI.report(reporter, %{
1607 account_id: target_user.id,
1608 comment: "I feel offended",
1609 status_ids: [activity.id]
1614 |> get("/api/pleroma/admin/reports")
1615 |> json_response(:ok)
1617 [report] = response["reports"]
1619 assert length(response["reports"]) == 1
1620 assert report["id"] == report_id
1622 assert response["total"] == 1
1625 test "returns reports with specified state", %{conn: conn} do
1626 [reporter, target_user] = insert_pair(:user)
1627 activity = insert(:note_activity, user: target_user)
1629 {:ok, %{id: first_report_id}} =
1630 CommonAPI.report(reporter, %{
1631 account_id: target_user.id,
1632 comment: "I feel offended",
1633 status_ids: [activity.id]
1636 {:ok, %{id: second_report_id}} =
1637 CommonAPI.report(reporter, %{
1638 account_id: target_user.id,
1639 comment: "I don't like this user"
1642 CommonAPI.update_report_state(second_report_id, "closed")
1646 |> get("/api/pleroma/admin/reports", %{
1649 |> json_response(:ok)
1651 [open_report] = response["reports"]
1653 assert length(response["reports"]) == 1
1654 assert open_report["id"] == first_report_id
1656 assert response["total"] == 1
1660 |> get("/api/pleroma/admin/reports", %{
1663 |> json_response(:ok)
1665 [closed_report] = response["reports"]
1667 assert length(response["reports"]) == 1
1668 assert closed_report["id"] == second_report_id
1670 assert response["total"] == 1
1674 |> get("/api/pleroma/admin/reports", %{
1675 "state" => "resolved"
1677 |> json_response(:ok)
1679 assert Enum.empty?(response["reports"])
1680 assert response["total"] == 0
1683 test "returns 403 when requested by a non-admin" do
1684 user = insert(:user)
1685 token = insert(:oauth_token, user: user)
1689 |> assign(:user, user)
1690 |> assign(:token, token)
1691 |> get("/api/pleroma/admin/reports")
1693 assert json_response(conn, :forbidden) ==
1694 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1697 test "returns 403 when requested by anonymous" do
1698 conn = get(build_conn(), "/api/pleroma/admin/reports")
1700 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1704 describe "GET /api/pleroma/admin/config" do
1705 setup do: clear_config(:configurable_from_database, true)
1707 test "when configuration from database is off", %{conn: conn} do
1708 Config.put(:configurable_from_database, false)
1709 conn = get(conn, "/api/pleroma/admin/config")
1711 assert json_response(conn, 400) ==
1713 "error" => "To use this endpoint you need to enable configuration from database."
1717 test "with settings only in db", %{conn: conn} do
1718 config1 = insert(:config)
1719 config2 = insert(:config)
1721 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1726 "group" => ":pleroma",
1731 "group" => ":pleroma",
1736 } = json_response(conn, 200)
1738 assert key1 == config1.key
1739 assert key2 == config2.key
1742 test "db is added to settings that are in db", %{conn: conn} do
1743 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1745 %{"configs" => configs} =
1747 |> get("/api/pleroma/admin/config")
1748 |> json_response(200)
1751 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1752 group == ":pleroma" and key == ":instance"
1755 assert instance_config["db"] == [":name"]
1758 test "merged default setting with db settings", %{conn: conn} do
1759 config1 = insert(:config)
1760 config2 = insert(:config)
1764 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1767 %{"configs" => configs} =
1769 |> get("/api/pleroma/admin/config")
1770 |> json_response(200)
1772 assert length(configs) > 3
1775 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1776 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1779 assert length(received_configs) == 3
1783 |> ConfigDB.from_binary()
1785 |> ConfigDB.convert()
1787 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1788 assert db in [[config1.key], [config2.key], db_keys]
1791 ConfigDB.from_binary_with_convert(config1.value),
1792 ConfigDB.from_binary_with_convert(config2.value),
1793 ConfigDB.from_binary_with_convert(config3.value)
1798 test "subkeys with full update right merge", %{conn: conn} do
1802 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1808 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1811 %{"configs" => configs} =
1813 |> get("/api/pleroma/admin/config")
1814 |> json_response(200)
1817 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1818 group == ":pleroma" and key in [config1.key, config2.key]
1821 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
1822 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
1824 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
1825 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
1827 assert emoji_val[:groups] == [a: 1, b: 2]
1828 assert assets_val[:mascots] == [a: 1, b: 2]
1832 test "POST /api/pleroma/admin/config error", %{conn: conn} do
1833 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
1835 assert json_response(conn, 400) ==
1836 %{"error" => "To use this endpoint you need to enable configuration from database."}
1839 describe "POST /api/pleroma/admin/config" do
1841 http = Application.get_env(:pleroma, :http)
1844 Application.delete_env(:pleroma, :key1)
1845 Application.delete_env(:pleroma, :key2)
1846 Application.delete_env(:pleroma, :key3)
1847 Application.delete_env(:pleroma, :key4)
1848 Application.delete_env(:pleroma, :keyaa1)
1849 Application.delete_env(:pleroma, :keyaa2)
1850 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1851 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1852 Application.put_env(:pleroma, :http, http)
1853 Application.put_env(:tesla, :adapter, Tesla.Mock)
1854 Restarter.Pleroma.refresh()
1858 setup do: clear_config(:configurable_from_database, true)
1860 @tag capture_log: true
1861 test "create new config setting in db", %{conn: conn} do
1862 ueberauth = Application.get_env(:ueberauth, Ueberauth)
1863 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
1866 post(conn, "/api/pleroma/admin/config", %{
1868 %{group: ":pleroma", key: ":key1", value: "value1"},
1870 group: ":ueberauth",
1872 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1878 ":nested_1" => "nested_value1",
1880 %{":nested_22" => "nested_value222"},
1881 %{":nested_33" => %{":nested_44" => "nested_444"}}
1889 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1890 %{"nested_4" => true}
1896 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
1901 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
1906 assert json_response(conn, 200) == %{
1909 "group" => ":pleroma",
1911 "value" => "value1",
1915 "group" => ":ueberauth",
1916 "key" => "Ueberauth",
1917 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
1918 "db" => [":consumer_secret"]
1921 "group" => ":pleroma",
1924 ":nested_1" => "nested_value1",
1926 %{":nested_22" => "nested_value222"},
1927 %{":nested_33" => %{":nested_44" => "nested_444"}}
1933 "group" => ":pleroma",
1936 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1937 %{"nested_4" => true}
1942 "group" => ":pleroma",
1944 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
1950 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
1956 assert Application.get_env(:pleroma, :key1) == "value1"
1958 assert Application.get_env(:pleroma, :key2) == %{
1959 nested_1: "nested_value1",
1961 %{nested_22: "nested_value222"},
1962 %{nested_33: %{nested_44: "nested_444"}}
1966 assert Application.get_env(:pleroma, :key3) == [
1967 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
1968 %{"nested_4" => true}
1971 assert Application.get_env(:pleroma, :key4) == %{
1972 "endpoint" => "https://example.com",
1976 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
1979 test "save configs setting without explicit key", %{conn: conn} do
1980 level = Application.get_env(:quack, :level)
1981 meta = Application.get_env(:quack, :meta)
1982 webhook_url = Application.get_env(:quack, :webhook_url)
1985 Application.put_env(:quack, :level, level)
1986 Application.put_env(:quack, :meta, meta)
1987 Application.put_env(:quack, :webhook_url, webhook_url)
1991 post(conn, "/api/pleroma/admin/config", %{
2005 key: ":webhook_url",
2006 value: "https://hooks.slack.com/services/KEY"
2011 assert json_response(conn, 200) == %{
2014 "group" => ":quack",
2020 "group" => ":quack",
2022 "value" => [":none"],
2026 "group" => ":quack",
2027 "key" => ":webhook_url",
2028 "value" => "https://hooks.slack.com/services/KEY",
2029 "db" => [":webhook_url"]
2034 assert Application.get_env(:quack, :level) == :info
2035 assert Application.get_env(:quack, :meta) == [:none]
2036 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
2039 test "saving config with partial update", %{conn: conn} do
2040 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
2043 post(conn, "/api/pleroma/admin/config", %{
2045 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
2049 assert json_response(conn, 200) == %{
2052 "group" => ":pleroma",
2055 %{"tuple" => [":key1", 1]},
2056 %{"tuple" => [":key2", 2]},
2057 %{"tuple" => [":key3", 3]}
2059 "db" => [":key1", ":key2", ":key3"]
2065 test "saving config which need pleroma reboot", %{conn: conn} do
2066 chat = Config.get(:chat)
2067 on_exit(fn -> Config.put(:chat, chat) end)
2071 "/api/pleroma/admin/config",
2074 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2078 |> json_response(200) == %{
2081 "db" => [":enabled"],
2082 "group" => ":pleroma",
2084 "value" => [%{"tuple" => [":enabled", true]}]
2087 "need_reboot" => true
2092 |> get("/api/pleroma/admin/config")
2093 |> json_response(200)
2095 assert configs["need_reboot"]
2098 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2099 end) =~ "pleroma restarted"
2103 |> get("/api/pleroma/admin/config")
2104 |> json_response(200)
2106 assert configs["need_reboot"] == false
2109 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
2110 chat = Config.get(:chat)
2111 on_exit(fn -> Config.put(:chat, chat) end)
2115 "/api/pleroma/admin/config",
2118 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2122 |> json_response(200) == %{
2125 "db" => [":enabled"],
2126 "group" => ":pleroma",
2128 "value" => [%{"tuple" => [":enabled", true]}]
2131 "need_reboot" => true
2134 assert post(conn, "/api/pleroma/admin/config", %{
2136 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
2139 |> json_response(200) == %{
2142 "group" => ":pleroma",
2145 %{"tuple" => [":key3", 3]}
2150 "need_reboot" => true
2154 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2155 end) =~ "pleroma restarted"
2159 |> get("/api/pleroma/admin/config")
2160 |> json_response(200)
2162 assert configs["need_reboot"] == false
2165 test "saving config with nested merge", %{conn: conn} do
2167 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
2170 post(conn, "/api/pleroma/admin/config", %{
2173 group: config.group,
2176 %{"tuple" => [":key3", 3]},
2181 %{"tuple" => [":k2", 1]},
2182 %{"tuple" => [":k3", 3]}
2191 assert json_response(conn, 200) == %{
2194 "group" => ":pleroma",
2197 %{"tuple" => [":key1", 1]},
2198 %{"tuple" => [":key3", 3]},
2203 %{"tuple" => [":k1", 1]},
2204 %{"tuple" => [":k2", 1]},
2205 %{"tuple" => [":k3", 3]}
2210 "db" => [":key1", ":key3", ":key2"]
2216 test "saving special atoms", %{conn: conn} do
2218 post(conn, "/api/pleroma/admin/config", %{
2221 "group" => ":pleroma",
2227 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2235 assert json_response(conn, 200) == %{
2238 "group" => ":pleroma",
2244 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2248 "db" => [":ssl_options"]
2253 assert Application.get_env(:pleroma, :key1) == [
2254 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
2258 test "saving full setting if value is in full_key_update list", %{conn: conn} do
2259 backends = Application.get_env(:logger, :backends)
2260 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
2266 value: :erlang.term_to_binary([])
2269 Pleroma.Config.TransferTask.load_and_update_env([], false)
2271 assert Application.get_env(:logger, :backends) == []
2274 post(conn, "/api/pleroma/admin/config", %{
2277 group: config.group,
2284 assert json_response(conn, 200) == %{
2287 "group" => ":logger",
2288 "key" => ":backends",
2292 "db" => [":backends"]
2297 assert Application.get_env(:logger, :backends) == [
2302 test "saving full setting if value is not keyword", %{conn: conn} do
2307 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
2311 post(conn, "/api/pleroma/admin/config", %{
2313 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
2317 assert json_response(conn, 200) == %{
2320 "group" => ":tesla",
2321 "key" => ":adapter",
2322 "value" => "Tesla.Adapter.Httpc",
2323 "db" => [":adapter"]
2329 test "update config setting & delete with fallback to default value", %{
2334 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2335 config1 = insert(:config, key: ":keyaa1")
2336 config2 = insert(:config, key: ":keyaa2")
2340 group: ":ueberauth",
2345 post(conn, "/api/pleroma/admin/config", %{
2347 %{group: config1.group, key: config1.key, value: "another_value"},
2348 %{group: config2.group, key: config2.key, value: "another_value"}
2352 assert json_response(conn, 200) == %{
2355 "group" => ":pleroma",
2356 "key" => config1.key,
2357 "value" => "another_value",
2361 "group" => ":pleroma",
2362 "key" => config2.key,
2363 "value" => "another_value",
2369 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2370 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
2371 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
2375 |> assign(:user, admin)
2376 |> assign(:token, token)
2377 |> post("/api/pleroma/admin/config", %{
2379 %{group: config2.group, key: config2.key, delete: true},
2381 group: ":ueberauth",
2388 assert json_response(conn, 200) == %{
2392 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
2393 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
2396 test "common config example", %{conn: conn} do
2398 post(conn, "/api/pleroma/admin/config", %{
2401 "group" => ":pleroma",
2402 "key" => "Pleroma.Captcha.NotReal",
2404 %{"tuple" => [":enabled", false]},
2405 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2406 %{"tuple" => [":seconds_valid", 60]},
2407 %{"tuple" => [":path", ""]},
2408 %{"tuple" => [":key1", nil]},
2409 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2410 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2411 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2412 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2413 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2414 %{"tuple" => [":name", "Pleroma"]}
2420 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2422 assert json_response(conn, 200) == %{
2425 "group" => ":pleroma",
2426 "key" => "Pleroma.Captcha.NotReal",
2428 %{"tuple" => [":enabled", false]},
2429 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2430 %{"tuple" => [":seconds_valid", 60]},
2431 %{"tuple" => [":path", ""]},
2432 %{"tuple" => [":key1", nil]},
2433 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2434 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2435 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2436 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2437 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2438 %{"tuple" => [":name", "Pleroma"]}
2458 test "tuples with more than two values", %{conn: conn} do
2460 post(conn, "/api/pleroma/admin/config", %{
2463 "group" => ":pleroma",
2464 "key" => "Pleroma.Web.Endpoint.NotReal",
2480 "/api/v1/streaming",
2481 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2488 "Phoenix.Endpoint.CowboyWebSocket",
2491 "Phoenix.Transports.WebSocket",
2494 "Pleroma.Web.Endpoint",
2495 "Pleroma.Web.UserSocket",
2506 "Phoenix.Endpoint.Cowboy2Handler",
2507 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2524 assert json_response(conn, 200) == %{
2527 "group" => ":pleroma",
2528 "key" => "Pleroma.Web.Endpoint.NotReal",
2544 "/api/v1/streaming",
2545 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2552 "Phoenix.Endpoint.CowboyWebSocket",
2555 "Phoenix.Transports.WebSocket",
2558 "Pleroma.Web.Endpoint",
2559 "Pleroma.Web.UserSocket",
2570 "Phoenix.Endpoint.Cowboy2Handler",
2571 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2590 test "settings with nesting map", %{conn: conn} do
2592 post(conn, "/api/pleroma/admin/config", %{
2595 "group" => ":pleroma",
2598 %{"tuple" => [":key2", "some_val"]},
2603 ":max_options" => 20,
2604 ":max_option_chars" => 200,
2605 ":min_expiration" => 0,
2606 ":max_expiration" => 31_536_000,
2608 ":max_options" => 20,
2609 ":max_option_chars" => 200,
2610 ":min_expiration" => 0,
2611 ":max_expiration" => 31_536_000
2621 assert json_response(conn, 200) ==
2625 "group" => ":pleroma",
2628 %{"tuple" => [":key2", "some_val"]},
2633 ":max_expiration" => 31_536_000,
2634 ":max_option_chars" => 200,
2635 ":max_options" => 20,
2636 ":min_expiration" => 0,
2638 ":max_expiration" => 31_536_000,
2639 ":max_option_chars" => 200,
2640 ":max_options" => 20,
2641 ":min_expiration" => 0
2647 "db" => [":key2", ":key3"]
2653 test "value as map", %{conn: conn} do
2655 post(conn, "/api/pleroma/admin/config", %{
2658 "group" => ":pleroma",
2660 "value" => %{"key" => "some_val"}
2665 assert json_response(conn, 200) ==
2669 "group" => ":pleroma",
2671 "value" => %{"key" => "some_val"},
2678 test "queues key as atom", %{conn: conn} do
2680 post(conn, "/api/pleroma/admin/config", %{
2686 %{"tuple" => [":federator_incoming", 50]},
2687 %{"tuple" => [":federator_outgoing", 50]},
2688 %{"tuple" => [":web_push", 50]},
2689 %{"tuple" => [":mailer", 10]},
2690 %{"tuple" => [":transmogrifier", 20]},
2691 %{"tuple" => [":scheduled_activities", 10]},
2692 %{"tuple" => [":background", 5]}
2698 assert json_response(conn, 200) == %{
2704 %{"tuple" => [":federator_incoming", 50]},
2705 %{"tuple" => [":federator_outgoing", 50]},
2706 %{"tuple" => [":web_push", 50]},
2707 %{"tuple" => [":mailer", 10]},
2708 %{"tuple" => [":transmogrifier", 20]},
2709 %{"tuple" => [":scheduled_activities", 10]},
2710 %{"tuple" => [":background", 5]}
2713 ":federator_incoming",
2714 ":federator_outgoing",
2718 ":scheduled_activities",
2726 test "delete part of settings by atom subkeys", %{conn: conn} do
2730 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2734 post(conn, "/api/pleroma/admin/config", %{
2737 group: config.group,
2739 subkeys: [":subkey1", ":subkey3"],
2745 assert json_response(conn, 200) == %{
2748 "group" => ":pleroma",
2750 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2751 "db" => [":subkey2"]
2757 test "proxy tuple localhost", %{conn: conn} do
2759 post(conn, "/api/pleroma/admin/config", %{
2765 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
2774 "group" => ":pleroma",
2780 } = json_response(conn, 200)
2782 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
2783 assert ":proxy_url" in db
2786 test "proxy tuple domain", %{conn: conn} do
2788 post(conn, "/api/pleroma/admin/config", %{
2794 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
2803 "group" => ":pleroma",
2809 } = json_response(conn, 200)
2811 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
2812 assert ":proxy_url" in db
2815 test "proxy tuple ip", %{conn: conn} do
2817 post(conn, "/api/pleroma/admin/config", %{
2823 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
2832 "group" => ":pleroma",
2838 } = json_response(conn, 200)
2840 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
2841 assert ":proxy_url" in db
2844 @tag capture_log: true
2845 test "doesn't set keys not in the whitelist", %{conn: conn} do
2846 clear_config(:database_config_whitelist, [
2849 {:pleroma, Pleroma.Captcha.NotReal},
2853 post(conn, "/api/pleroma/admin/config", %{
2855 %{group: ":pleroma", key: ":key1", value: "value1"},
2856 %{group: ":pleroma", key: ":key2", value: "value2"},
2857 %{group: ":pleroma", key: ":key3", value: "value3"},
2858 %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
2859 %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
2860 %{group: ":not_real", key: ":anything", value: "value6"}
2864 assert Application.get_env(:pleroma, :key1) == "value1"
2865 assert Application.get_env(:pleroma, :key2) == "value2"
2866 assert Application.get_env(:pleroma, :key3) == nil
2867 assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
2868 assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
2869 assert Application.get_env(:not_real, :anything) == "value6"
2873 describe "GET /api/pleroma/admin/restart" do
2874 setup do: clear_config(:configurable_from_database, true)
2876 test "pleroma restarts", %{conn: conn} do
2878 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2879 end) =~ "pleroma restarted"
2881 refute Restarter.Pleroma.need_reboot?()
2885 test "need_reboot flag", %{conn: conn} do
2887 |> get("/api/pleroma/admin/need_reboot")
2888 |> json_response(200) == %{"need_reboot" => false}
2890 Restarter.Pleroma.need_reboot()
2893 |> get("/api/pleroma/admin/need_reboot")
2894 |> json_response(200) == %{"need_reboot" => true}
2896 on_exit(fn -> Restarter.Pleroma.refresh() end)
2899 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2901 user = insert(:user)
2903 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2904 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2905 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2907 insert(:note_activity, user: user, published: date1)
2908 insert(:note_activity, user: user, published: date2)
2909 insert(:note_activity, user: user, published: date3)
2914 test "renders user's statuses", %{conn: conn, user: user} do
2915 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2917 assert json_response(conn, 200) |> length() == 3
2920 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2921 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2923 assert json_response(conn, 200) |> length() == 2
2926 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2927 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
2929 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
2931 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2933 assert json_response(conn, 200) |> length() == 4
2936 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2937 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
2939 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
2941 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2943 assert json_response(conn, 200) |> length() == 5
2946 test "excludes reblogs by default", %{conn: conn, user: user} do
2947 other_user = insert(:user)
2948 {:ok, activity} = CommonAPI.post(user, %{status: "."})
2949 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
2951 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
2952 assert json_response(conn_res, 200) |> length() == 0
2955 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
2957 assert json_response(conn_res, 200) |> length() == 1
2961 describe "GET /api/pleroma/admin/moderation_log" do
2963 moderator = insert(:user, is_moderator: true)
2965 %{moderator: moderator}
2968 test "returns the log", %{conn: conn, admin: admin} do
2969 Repo.insert(%ModerationLog{
2973 "nickname" => admin.nickname,
2976 action: "relay_follow",
2977 target: "https://example.org/relay"
2979 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2982 Repo.insert(%ModerationLog{
2986 "nickname" => admin.nickname,
2989 action: "relay_unfollow",
2990 target: "https://example.org/relay"
2992 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2995 conn = get(conn, "/api/pleroma/admin/moderation_log")
2997 response = json_response(conn, 200)
2998 [first_entry, second_entry] = response["items"]
3000 assert response["total"] == 2
3001 assert first_entry["data"]["action"] == "relay_unfollow"
3003 assert first_entry["message"] ==
3004 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3006 assert second_entry["data"]["action"] == "relay_follow"
3008 assert second_entry["message"] ==
3009 "@#{admin.nickname} followed relay: https://example.org/relay"
3012 test "returns the log with pagination", %{conn: conn, admin: admin} do
3013 Repo.insert(%ModerationLog{
3017 "nickname" => admin.nickname,
3020 action: "relay_follow",
3021 target: "https://example.org/relay"
3023 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3026 Repo.insert(%ModerationLog{
3030 "nickname" => admin.nickname,
3033 action: "relay_unfollow",
3034 target: "https://example.org/relay"
3036 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3039 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3041 response1 = json_response(conn1, 200)
3042 [first_entry] = response1["items"]
3044 assert response1["total"] == 2
3045 assert response1["items"] |> length() == 1
3046 assert first_entry["data"]["action"] == "relay_unfollow"
3048 assert first_entry["message"] ==
3049 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3051 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3053 response2 = json_response(conn2, 200)
3054 [second_entry] = response2["items"]
3056 assert response2["total"] == 2
3057 assert response2["items"] |> length() == 1
3058 assert second_entry["data"]["action"] == "relay_follow"
3060 assert second_entry["message"] ==
3061 "@#{admin.nickname} followed relay: https://example.org/relay"
3064 test "filters log by date", %{conn: conn, admin: admin} do
3065 first_date = "2017-08-15T15:47:06Z"
3066 second_date = "2017-08-20T15:47:06Z"
3068 Repo.insert(%ModerationLog{
3072 "nickname" => admin.nickname,
3075 action: "relay_follow",
3076 target: "https://example.org/relay"
3078 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3081 Repo.insert(%ModerationLog{
3085 "nickname" => admin.nickname,
3088 action: "relay_unfollow",
3089 target: "https://example.org/relay"
3091 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3097 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3100 response1 = json_response(conn1, 200)
3101 [first_entry] = response1["items"]
3103 assert response1["total"] == 1
3104 assert first_entry["data"]["action"] == "relay_unfollow"
3106 assert first_entry["message"] ==
3107 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3110 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3111 Repo.insert(%ModerationLog{
3115 "nickname" => admin.nickname,
3118 action: "relay_follow",
3119 target: "https://example.org/relay"
3123 Repo.insert(%ModerationLog{
3126 "id" => moderator.id,
3127 "nickname" => moderator.nickname,
3130 action: "relay_unfollow",
3131 target: "https://example.org/relay"
3135 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3137 response1 = json_response(conn1, 200)
3138 [first_entry] = response1["items"]
3140 assert response1["total"] == 1
3141 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3144 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3145 ModerationLog.insert_log(%{
3147 action: "relay_follow",
3148 target: "https://example.org/relay"
3151 ModerationLog.insert_log(%{
3153 action: "relay_unfollow",
3154 target: "https://example.org/relay"
3157 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3159 response1 = json_response(conn1, 200)
3160 [first_entry] = response1["items"]
3162 assert response1["total"] == 1
3164 assert get_in(first_entry, ["data", "message"]) ==
3165 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3169 describe "GET /users/:nickname/credentials" do
3170 test "gets the user credentials", %{conn: conn} do
3171 user = insert(:user)
3172 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3174 response = assert json_response(conn, 200)
3175 assert response["email"] == user.email
3178 test "returns 403 if requested by a non-admin" do
3179 user = insert(:user)
3183 |> assign(:user, user)
3184 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3186 assert json_response(conn, :forbidden)
3190 describe "PATCH /users/:nickname/credentials" do
3192 user = insert(:user)
3196 test "changes password and email", %{conn: conn, admin: admin, user: user} do
3197 assert user.password_reset_pending == false
3200 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3201 "password" => "new_password",
3202 "email" => "new_email@example.com",
3203 "name" => "new_name"
3206 assert json_response(conn, 200) == %{"status" => "success"}
3208 ObanHelpers.perform_all()
3210 updated_user = User.get_by_id(user.id)
3212 assert updated_user.email == "new_email@example.com"
3213 assert updated_user.name == "new_name"
3214 assert updated_user.password_hash != user.password_hash
3215 assert updated_user.password_reset_pending == true
3217 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3219 assert ModerationLog.get_log_entry_message(log_entry1) ==
3220 "@#{admin.nickname} updated users: @#{user.nickname}"
3222 assert ModerationLog.get_log_entry_message(log_entry2) ==
3223 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3226 test "returns 403 if requested by a non-admin", %{user: user} do
3229 |> assign(:user, user)
3230 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3231 "password" => "new_password",
3232 "email" => "new_email@example.com",
3233 "name" => "new_name"
3236 assert json_response(conn, :forbidden)
3239 test "changes actor type from permitted list", %{conn: conn, user: user} do
3240 assert user.actor_type == "Person"
3242 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3243 "actor_type" => "Service"
3245 |> json_response(200) == %{"status" => "success"}
3247 updated_user = User.get_by_id(user.id)
3249 assert updated_user.actor_type == "Service"
3251 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3252 "actor_type" => "Application"
3254 |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
3257 test "update non existing user", %{conn: conn} do
3258 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
3259 "password" => "new_password"
3261 |> json_response(200) == %{"error" => "Unable to update user."}
3265 describe "PATCH /users/:nickname/force_password_reset" do
3266 test "sets password_reset_pending to true", %{conn: conn} do
3267 user = insert(:user)
3268 assert user.password_reset_pending == false
3271 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3273 assert json_response(conn, 204) == ""
3275 ObanHelpers.perform_all()
3277 assert User.get_by_id(user.id).password_reset_pending == true
3281 describe "relays" do
3282 test "POST /relay", %{conn: conn, admin: admin} do
3284 post(conn, "/api/pleroma/admin/relay", %{
3285 relay_url: "http://mastodon.example.org/users/admin"
3288 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3290 log_entry = Repo.one(ModerationLog)
3292 assert ModerationLog.get_log_entry_message(log_entry) ==
3293 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3296 test "GET /relay", %{conn: conn} do
3297 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3299 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3300 |> Enum.each(fn ap_id ->
3301 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3302 User.follow(relay_user, user)
3305 conn = get(conn, "/api/pleroma/admin/relay")
3307 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3310 test "DELETE /relay", %{conn: conn, admin: admin} do
3311 post(conn, "/api/pleroma/admin/relay", %{
3312 relay_url: "http://mastodon.example.org/users/admin"
3316 delete(conn, "/api/pleroma/admin/relay", %{
3317 relay_url: "http://mastodon.example.org/users/admin"
3320 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3322 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3324 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3325 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3327 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3328 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3332 describe "instances" do
3333 test "GET /instances/:instance/statuses", %{conn: conn} do
3334 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3335 user2 = insert(:user, local: false, nickname: "test@test.com")
3336 insert_pair(:note_activity, user: user)
3337 activity = insert(:note_activity, user: user2)
3339 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3341 response = json_response(ret_conn, 200)
3343 assert length(response) == 2
3345 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3347 response = json_response(ret_conn, 200)
3349 assert length(response) == 1
3351 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3353 response = json_response(ret_conn, 200)
3355 assert Enum.empty?(response)
3357 CommonAPI.repeat(activity.id, user)
3359 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3360 response = json_response(ret_conn, 200)
3361 assert length(response) == 2
3363 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3364 response = json_response(ret_conn, 200)
3365 assert length(response) == 3
3369 describe "PATCH /confirm_email" do
3370 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3371 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3373 assert first_user.confirmation_pending == true
3374 assert second_user.confirmation_pending == true
3377 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3379 first_user.nickname,
3380 second_user.nickname
3384 assert ret_conn.status == 200
3386 assert first_user.confirmation_pending == true
3387 assert second_user.confirmation_pending == true
3389 log_entry = Repo.one(ModerationLog)
3391 assert ModerationLog.get_log_entry_message(log_entry) ==
3392 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3393 second_user.nickname
3398 describe "PATCH /resend_confirmation_email" do
3399 test "it resend emails for two users", %{conn: conn, admin: admin} do
3400 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3403 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3405 first_user.nickname,
3406 second_user.nickname
3410 assert ret_conn.status == 200
3412 log_entry = Repo.one(ModerationLog)
3414 assert ModerationLog.get_log_entry_message(log_entry) ==
3415 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3416 second_user.nickname
3421 describe "POST /reports/:id/notes" do
3422 setup %{conn: conn, admin: admin} do
3423 [reporter, target_user] = insert_pair(:user)
3424 activity = insert(:note_activity, user: target_user)
3426 {:ok, %{id: report_id}} =
3427 CommonAPI.report(reporter, %{
3428 account_id: target_user.id,
3429 comment: "I feel offended",
3430 status_ids: [activity.id]
3433 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3434 content: "this is disgusting!"
3437 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3438 content: "this is disgusting2!"
3443 report_id: report_id
3447 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3448 [note, _] = Repo.all(ReportNote)
3451 activity_id: ^report_id,
3452 content: "this is disgusting!",
3457 test "it returns reports with notes", %{conn: conn, admin: admin} do
3458 conn = get(conn, "/api/pleroma/admin/reports")
3460 response = json_response(conn, 200)
3461 notes = hd(response["reports"])["notes"]
3464 assert note["user"]["nickname"] == admin.nickname
3465 assert note["content"] == "this is disgusting!"
3466 assert note["created_at"]
3467 assert response["total"] == 1
3470 test "it deletes the note", %{conn: conn, report_id: report_id} do
3471 assert ReportNote |> Repo.all() |> length() == 2
3473 [note, _] = Repo.all(ReportNote)
3475 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3477 assert ReportNote |> Repo.all() |> length() == 1
3481 describe "GET /api/pleroma/admin/config/descriptions" do
3482 test "structure", %{conn: conn} do
3483 admin = insert(:user, is_admin: true)
3486 assign(conn, :user, admin)
3487 |> get("/api/pleroma/admin/config/descriptions")
3489 assert [child | _others] = json_response(conn, 200)
3491 assert child["children"]
3493 assert String.starts_with?(child["group"], ":")
3494 assert child["description"]
3497 test "filters by database configuration whitelist", %{conn: conn} do
3498 clear_config(:database_config_whitelist, [
3499 {:pleroma, :instance},
3500 {:pleroma, :activitypub},
3501 {:pleroma, Pleroma.Upload},
3505 admin = insert(:user, is_admin: true)
3508 assign(conn, :user, admin)
3509 |> get("/api/pleroma/admin/config/descriptions")
3511 children = json_response(conn, 200)
3513 assert length(children) == 4
3515 assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
3517 instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
3518 assert instance["children"]
3520 activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
3521 assert activitypub["children"]
3523 web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
3524 assert web_endpoint["children"]
3526 esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
3527 assert esshd["children"]
3531 describe "/api/pleroma/admin/stats" do
3532 test "status visibility count", %{conn: conn} do
3533 admin = insert(:user, is_admin: true)
3534 user = insert(:user)
3535 CommonAPI.post(user, %{visibility: "public", status: "hey"})
3536 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3537 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3541 |> assign(:user, admin)
3542 |> get("/api/pleroma/admin/stats")
3543 |> json_response(200)
3545 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3546 response["status_visibility"]
3550 describe "POST /api/pleroma/admin/oauth_app" do
3551 test "errors", %{conn: conn} do
3552 response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
3554 assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
3557 test "success", %{conn: conn} do
3558 base_url = Web.base_url()
3559 app_name = "Trusted app"
3563 |> post("/api/pleroma/admin/oauth_app", %{
3565 redirect_uris: base_url
3567 |> json_response(200)
3571 "client_secret" => _,
3572 "name" => ^app_name,
3573 "redirect_uri" => ^base_url,
3578 test "with trusted", %{conn: conn} do
3579 base_url = Web.base_url()
3580 app_name = "Trusted app"
3584 |> post("/api/pleroma/admin/oauth_app", %{
3586 redirect_uris: base_url,
3589 |> json_response(200)
3593 "client_secret" => _,
3594 "name" => ^app_name,
3595 "redirect_uri" => ^base_url,
3601 describe "GET /api/pleroma/admin/oauth_app" do
3603 app = insert(:oauth_app)
3607 test "list", %{conn: conn} do
3610 |> get("/api/pleroma/admin/oauth_app")
3611 |> json_response(200)
3613 assert %{"apps" => apps, "count" => count, "page_size" => _} = response
3615 assert length(apps) == count
3618 test "with page size", %{conn: conn} do
3624 |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
3625 |> json_response(200)
3627 assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
3629 assert length(apps) == page_size
3632 test "search by client name", %{conn: conn, app: app} do
3635 |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
3636 |> json_response(200)
3638 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3640 assert returned["client_id"] == app.client_id
3641 assert returned["name"] == app.client_name
3644 test "search by client id", %{conn: conn, app: app} do
3647 |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
3648 |> json_response(200)
3650 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3652 assert returned["client_id"] == app.client_id
3653 assert returned["name"] == app.client_name
3656 test "only trusted", %{conn: conn} do
3657 app = insert(:oauth_app, trusted: true)
3661 |> get("/api/pleroma/admin/oauth_app", %{trusted: true})
3662 |> json_response(200)
3664 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3666 assert returned["client_id"] == app.client_id
3667 assert returned["name"] == app.client_name
3671 describe "DELETE /api/pleroma/admin/oauth_app/:id" do
3672 test "with id", %{conn: conn} do
3673 app = insert(:oauth_app)
3677 |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
3678 |> json_response(:no_content)
3680 assert response == ""
3683 test "with non existance id", %{conn: conn} do
3686 |> delete("/api/pleroma/admin/oauth_app/0")
3687 |> json_response(:bad_request)
3689 assert response == ""
3693 describe "PATCH /api/pleroma/admin/oauth_app/:id" do
3694 test "with id", %{conn: conn} do
3695 app = insert(:oauth_app)
3697 name = "another name"
3698 url = "https://example.com"
3701 website = "http://website.com"
3705 |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
3712 |> json_response(200)
3716 "client_secret" => _,
3719 "redirect_uri" => ^url,
3721 "website" => ^website
3725 test "without id", %{conn: conn} do
3728 |> patch("/api/pleroma/admin/oauth_app/0")
3729 |> json_response(:bad_request)
3731 assert response == ""
3736 # Needed for testing
3737 defmodule Pleroma.Web.Endpoint.NotReal do
3740 defmodule Pleroma.Captcha.NotReal do