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 = insert(:user, ap_id: Web.base_url() <> "/relay")
761 service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch")
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]
771 assert service2 not in [users1]
773 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
775 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
776 |> json_response(200)
778 assert Enum.count(users2) == 10
779 assert service1 not in [users2]
780 assert service2 not in [users2]
782 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
784 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
785 |> json_response(200)
787 assert Enum.count(users3) == 6
788 assert service1 not in [users3]
789 assert service2 not in [users3]
792 test "renders empty array for the second page", %{conn: conn} do
795 conn = get(conn, "/api/pleroma/admin/users?page=2")
797 assert json_response(conn, 200) == %{
804 test "regular search", %{conn: conn} do
805 user = insert(:user, nickname: "bob")
807 conn = get(conn, "/api/pleroma/admin/users?query=bo")
809 assert json_response(conn, 200) == %{
814 "deactivated" => user.deactivated,
816 "nickname" => user.nickname,
817 "roles" => %{"admin" => false, "moderator" => false},
820 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
821 "display_name" => HTML.strip_tags(user.name || user.nickname),
822 "confirmation_pending" => false
828 test "search by domain", %{conn: conn} do
829 user = insert(:user, nickname: "nickname@domain.com")
832 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
834 assert json_response(conn, 200) == %{
839 "deactivated" => user.deactivated,
841 "nickname" => user.nickname,
842 "roles" => %{"admin" => false, "moderator" => false},
845 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
846 "display_name" => HTML.strip_tags(user.name || user.nickname),
847 "confirmation_pending" => false
853 test "search by full nickname", %{conn: conn} do
854 user = insert(:user, nickname: "nickname@domain.com")
857 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
859 assert json_response(conn, 200) == %{
864 "deactivated" => user.deactivated,
866 "nickname" => user.nickname,
867 "roles" => %{"admin" => false, "moderator" => false},
870 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
871 "display_name" => HTML.strip_tags(user.name || user.nickname),
872 "confirmation_pending" => false
878 test "search by display name", %{conn: conn} do
879 user = insert(:user, name: "Display name")
882 conn = get(conn, "/api/pleroma/admin/users?name=display")
884 assert json_response(conn, 200) == %{
889 "deactivated" => user.deactivated,
891 "nickname" => user.nickname,
892 "roles" => %{"admin" => false, "moderator" => false},
895 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
896 "display_name" => HTML.strip_tags(user.name || user.nickname),
897 "confirmation_pending" => false
903 test "search by email", %{conn: conn} do
904 user = insert(:user, email: "email@example.com")
907 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
909 assert json_response(conn, 200) == %{
914 "deactivated" => user.deactivated,
916 "nickname" => user.nickname,
917 "roles" => %{"admin" => false, "moderator" => false},
920 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
921 "display_name" => HTML.strip_tags(user.name || user.nickname),
922 "confirmation_pending" => false
928 test "regular search with page size", %{conn: conn} do
929 user = insert(:user, nickname: "aalice")
930 user2 = insert(:user, nickname: "alice")
932 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
934 assert json_response(conn1, 200) == %{
939 "deactivated" => user.deactivated,
941 "nickname" => user.nickname,
942 "roles" => %{"admin" => false, "moderator" => false},
945 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
946 "display_name" => HTML.strip_tags(user.name || user.nickname),
947 "confirmation_pending" => false
952 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
954 assert json_response(conn2, 200) == %{
959 "deactivated" => user2.deactivated,
961 "nickname" => user2.nickname,
962 "roles" => %{"admin" => false, "moderator" => false},
965 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
966 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
967 "confirmation_pending" => false
973 test "only local users" do
974 admin = insert(:user, is_admin: true, nickname: "john")
975 token = insert(:oauth_admin_token, user: admin)
976 user = insert(:user, nickname: "bob")
978 insert(:user, nickname: "bobb", local: false)
982 |> assign(:user, admin)
983 |> assign(:token, token)
984 |> get("/api/pleroma/admin/users?query=bo&filters=local")
986 assert json_response(conn, 200) == %{
991 "deactivated" => user.deactivated,
993 "nickname" => user.nickname,
994 "roles" => %{"admin" => false, "moderator" => false},
997 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
998 "display_name" => HTML.strip_tags(user.name || user.nickname),
999 "confirmation_pending" => false
1005 test "only local users with no query", %{conn: conn, admin: old_admin} do
1006 admin = insert(:user, is_admin: true, nickname: "john")
1007 user = insert(:user, nickname: "bob")
1009 insert(:user, nickname: "bobb", local: false)
1011 conn = get(conn, "/api/pleroma/admin/users?filters=local")
1016 "deactivated" => user.deactivated,
1018 "nickname" => user.nickname,
1019 "roles" => %{"admin" => false, "moderator" => false},
1022 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1023 "display_name" => HTML.strip_tags(user.name || user.nickname),
1024 "confirmation_pending" => false
1027 "deactivated" => admin.deactivated,
1029 "nickname" => admin.nickname,
1030 "roles" => %{"admin" => true, "moderator" => false},
1033 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1034 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1035 "confirmation_pending" => false
1038 "deactivated" => false,
1039 "id" => old_admin.id,
1041 "nickname" => old_admin.nickname,
1042 "roles" => %{"admin" => true, "moderator" => false},
1044 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
1045 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
1046 "confirmation_pending" => false
1049 |> Enum.sort_by(& &1["nickname"])
1051 assert json_response(conn, 200) == %{
1058 test "load only admins", %{conn: conn, admin: admin} do
1059 second_admin = insert(:user, is_admin: true)
1063 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
1068 "deactivated" => false,
1070 "nickname" => admin.nickname,
1071 "roles" => %{"admin" => true, "moderator" => false},
1072 "local" => admin.local,
1074 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1075 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1076 "confirmation_pending" => false
1079 "deactivated" => false,
1080 "id" => second_admin.id,
1081 "nickname" => second_admin.nickname,
1082 "roles" => %{"admin" => true, "moderator" => false},
1083 "local" => second_admin.local,
1085 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1086 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1087 "confirmation_pending" => false
1090 |> Enum.sort_by(& &1["nickname"])
1092 assert json_response(conn, 200) == %{
1099 test "load only moderators", %{conn: conn} do
1100 moderator = insert(:user, is_moderator: true)
1104 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1106 assert json_response(conn, 200) == %{
1111 "deactivated" => false,
1112 "id" => moderator.id,
1113 "nickname" => moderator.nickname,
1114 "roles" => %{"admin" => false, "moderator" => true},
1115 "local" => moderator.local,
1117 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1118 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1119 "confirmation_pending" => false
1125 test "load users with tags list", %{conn: conn} do
1126 user1 = insert(:user, tags: ["first"])
1127 user2 = insert(:user, tags: ["second"])
1131 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1136 "deactivated" => false,
1138 "nickname" => user1.nickname,
1139 "roles" => %{"admin" => false, "moderator" => false},
1140 "local" => user1.local,
1141 "tags" => ["first"],
1142 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1143 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1144 "confirmation_pending" => false
1147 "deactivated" => false,
1149 "nickname" => user2.nickname,
1150 "roles" => %{"admin" => false, "moderator" => false},
1151 "local" => user2.local,
1152 "tags" => ["second"],
1153 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1154 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1155 "confirmation_pending" => false
1158 |> Enum.sort_by(& &1["nickname"])
1160 assert json_response(conn, 200) == %{
1167 test "it works with multiple filters" do
1168 admin = insert(:user, nickname: "john", is_admin: true)
1169 token = insert(:oauth_admin_token, user: admin)
1170 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1172 insert(:user, nickname: "ken", local: true, deactivated: true)
1173 insert(:user, nickname: "bobb", local: false, deactivated: false)
1177 |> assign(:user, admin)
1178 |> assign(:token, token)
1179 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1181 assert json_response(conn, 200) == %{
1186 "deactivated" => user.deactivated,
1188 "nickname" => user.nickname,
1189 "roles" => %{"admin" => false, "moderator" => false},
1190 "local" => user.local,
1192 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1193 "display_name" => HTML.strip_tags(user.name || user.nickname),
1194 "confirmation_pending" => false
1200 test "it omits relay user", %{admin: admin, conn: conn} do
1201 assert %User{} = Relay.get_actor()
1203 conn = get(conn, "/api/pleroma/admin/users")
1205 assert json_response(conn, 200) == %{
1210 "deactivated" => admin.deactivated,
1212 "nickname" => admin.nickname,
1213 "roles" => %{"admin" => true, "moderator" => false},
1216 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1217 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1218 "confirmation_pending" => false
1225 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1226 user_one = insert(:user, deactivated: true)
1227 user_two = insert(:user, deactivated: true)
1232 "/api/pleroma/admin/users/activate",
1233 %{nicknames: [user_one.nickname, user_two.nickname]}
1236 response = json_response(conn, 200)
1237 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1239 log_entry = Repo.one(ModerationLog)
1241 assert ModerationLog.get_log_entry_message(log_entry) ==
1242 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1245 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1246 user_one = insert(:user, deactivated: false)
1247 user_two = insert(:user, deactivated: false)
1252 "/api/pleroma/admin/users/deactivate",
1253 %{nicknames: [user_one.nickname, user_two.nickname]}
1256 response = json_response(conn, 200)
1257 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1259 log_entry = Repo.one(ModerationLog)
1261 assert ModerationLog.get_log_entry_message(log_entry) ==
1262 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1265 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1266 user = insert(:user)
1268 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1270 assert json_response(conn, 200) ==
1272 "deactivated" => !user.deactivated,
1274 "nickname" => user.nickname,
1275 "roles" => %{"admin" => false, "moderator" => false},
1278 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1279 "display_name" => HTML.strip_tags(user.name || user.nickname),
1280 "confirmation_pending" => false
1283 log_entry = Repo.one(ModerationLog)
1285 assert ModerationLog.get_log_entry_message(log_entry) ==
1286 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1289 describe "PUT disable_mfa" do
1290 test "returns 200 and disable 2fa", %{conn: conn} do
1293 multi_factor_authentication_settings: %MFA.Settings{
1295 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1301 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1302 |> json_response(200)
1304 assert response == user.nickname
1305 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1307 refute mfa_settings.enabled
1308 refute mfa_settings.totp.confirmed
1311 test "returns 404 if user not found", %{conn: conn} do
1314 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1315 |> json_response(404)
1317 assert response == %{"error" => "Not found"}
1321 describe "POST /api/pleroma/admin/users/invite_token" do
1322 test "without options", %{conn: conn} do
1323 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1325 invite_json = json_response(conn, 200)
1326 invite = UserInviteToken.find_by_token!(invite_json["token"])
1328 refute invite.expires_at
1329 refute invite.max_use
1330 assert invite.invite_type == "one_time"
1333 test "with expires_at", %{conn: conn} do
1335 post(conn, "/api/pleroma/admin/users/invite_token", %{
1336 "expires_at" => Date.to_string(Date.utc_today())
1339 invite_json = json_response(conn, 200)
1340 invite = UserInviteToken.find_by_token!(invite_json["token"])
1343 assert invite.expires_at == Date.utc_today()
1344 refute invite.max_use
1345 assert invite.invite_type == "date_limited"
1348 test "with max_use", %{conn: conn} do
1349 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1351 invite_json = json_response(conn, 200)
1352 invite = UserInviteToken.find_by_token!(invite_json["token"])
1354 refute invite.expires_at
1355 assert invite.max_use == 150
1356 assert invite.invite_type == "reusable"
1359 test "with max use and expires_at", %{conn: conn} do
1361 post(conn, "/api/pleroma/admin/users/invite_token", %{
1363 "expires_at" => Date.to_string(Date.utc_today())
1366 invite_json = json_response(conn, 200)
1367 invite = UserInviteToken.find_by_token!(invite_json["token"])
1369 assert invite.expires_at == Date.utc_today()
1370 assert invite.max_use == 150
1371 assert invite.invite_type == "reusable_date_limited"
1375 describe "GET /api/pleroma/admin/users/invites" do
1376 test "no invites", %{conn: conn} do
1377 conn = get(conn, "/api/pleroma/admin/users/invites")
1379 assert json_response(conn, 200) == %{"invites" => []}
1382 test "with invite", %{conn: conn} do
1383 {:ok, invite} = UserInviteToken.create_invite()
1385 conn = get(conn, "/api/pleroma/admin/users/invites")
1387 assert json_response(conn, 200) == %{
1390 "expires_at" => nil,
1392 "invite_type" => "one_time",
1394 "token" => invite.token,
1403 describe "POST /api/pleroma/admin/users/revoke_invite" do
1404 test "with token", %{conn: conn} do
1405 {:ok, invite} = UserInviteToken.create_invite()
1407 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1409 assert json_response(conn, 200) == %{
1410 "expires_at" => nil,
1412 "invite_type" => "one_time",
1414 "token" => invite.token,
1420 test "with invalid token", %{conn: conn} do
1421 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1423 assert json_response(conn, :not_found) == %{"error" => "Not found"}
1427 describe "GET /api/pleroma/admin/reports/:id" do
1428 test "returns report by its id", %{conn: conn} do
1429 [reporter, target_user] = insert_pair(:user)
1430 activity = insert(:note_activity, user: target_user)
1432 {:ok, %{id: report_id}} =
1433 CommonAPI.report(reporter, %{
1434 account_id: target_user.id,
1435 comment: "I feel offended",
1436 status_ids: [activity.id]
1441 |> get("/api/pleroma/admin/reports/#{report_id}")
1442 |> json_response(:ok)
1444 assert response["id"] == report_id
1447 test "returns 404 when report id is invalid", %{conn: conn} do
1448 conn = get(conn, "/api/pleroma/admin/reports/test")
1450 assert json_response(conn, :not_found) == %{"error" => "Not found"}
1454 describe "PATCH /api/pleroma/admin/reports" do
1456 [reporter, target_user] = insert_pair(:user)
1457 activity = insert(:note_activity, user: target_user)
1459 {:ok, %{id: report_id}} =
1460 CommonAPI.report(reporter, %{
1461 account_id: target_user.id,
1462 comment: "I feel offended",
1463 status_ids: [activity.id]
1466 {:ok, %{id: second_report_id}} =
1467 CommonAPI.report(reporter, %{
1468 account_id: target_user.id,
1469 comment: "I feel very offended",
1470 status_ids: [activity.id]
1475 second_report_id: second_report_id
1479 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1480 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1481 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1485 |> assign(:token, read_token)
1486 |> patch("/api/pleroma/admin/reports", %{
1487 "reports" => [%{"state" => "resolved", "id" => id}]
1489 |> json_response(403)
1491 assert response == %{
1492 "error" => "Insufficient permissions: admin:write:reports."
1496 |> assign(:token, write_token)
1497 |> patch("/api/pleroma/admin/reports", %{
1498 "reports" => [%{"state" => "resolved", "id" => id}]
1500 |> json_response(:no_content)
1503 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1505 |> patch("/api/pleroma/admin/reports", %{
1507 %{"state" => "resolved", "id" => id}
1510 |> json_response(:no_content)
1512 activity = Activity.get_by_id(id)
1513 assert activity.data["state"] == "resolved"
1515 log_entry = Repo.one(ModerationLog)
1517 assert ModerationLog.get_log_entry_message(log_entry) ==
1518 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1521 test "closes report", %{conn: conn, id: id, admin: admin} do
1523 |> patch("/api/pleroma/admin/reports", %{
1525 %{"state" => "closed", "id" => id}
1528 |> json_response(:no_content)
1530 activity = Activity.get_by_id(id)
1531 assert activity.data["state"] == "closed"
1533 log_entry = Repo.one(ModerationLog)
1535 assert ModerationLog.get_log_entry_message(log_entry) ==
1536 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1539 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1542 |> patch("/api/pleroma/admin/reports", %{
1544 %{"state" => "test", "id" => id}
1548 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1551 test "returns 404 when report is not exist", %{conn: conn} do
1554 |> patch("/api/pleroma/admin/reports", %{
1556 %{"state" => "closed", "id" => "test"}
1560 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1563 test "updates state of multiple reports", %{
1567 second_report_id: second_report_id
1570 |> patch("/api/pleroma/admin/reports", %{
1572 %{"state" => "resolved", "id" => id},
1573 %{"state" => "closed", "id" => second_report_id}
1576 |> json_response(:no_content)
1578 activity = Activity.get_by_id(id)
1579 second_activity = Activity.get_by_id(second_report_id)
1580 assert activity.data["state"] == "resolved"
1581 assert second_activity.data["state"] == "closed"
1583 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1585 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1586 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1588 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1589 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1593 describe "GET /api/pleroma/admin/reports" do
1594 test "returns empty response when no reports created", %{conn: conn} do
1597 |> get("/api/pleroma/admin/reports")
1598 |> json_response(:ok)
1600 assert Enum.empty?(response["reports"])
1601 assert response["total"] == 0
1604 test "returns reports", %{conn: conn} do
1605 [reporter, target_user] = insert_pair(:user)
1606 activity = insert(:note_activity, user: target_user)
1608 {:ok, %{id: report_id}} =
1609 CommonAPI.report(reporter, %{
1610 account_id: target_user.id,
1611 comment: "I feel offended",
1612 status_ids: [activity.id]
1617 |> get("/api/pleroma/admin/reports")
1618 |> json_response(:ok)
1620 [report] = response["reports"]
1622 assert length(response["reports"]) == 1
1623 assert report["id"] == report_id
1625 assert response["total"] == 1
1628 test "returns reports with specified state", %{conn: conn} do
1629 [reporter, target_user] = insert_pair(:user)
1630 activity = insert(:note_activity, user: target_user)
1632 {:ok, %{id: first_report_id}} =
1633 CommonAPI.report(reporter, %{
1634 account_id: target_user.id,
1635 comment: "I feel offended",
1636 status_ids: [activity.id]
1639 {:ok, %{id: second_report_id}} =
1640 CommonAPI.report(reporter, %{
1641 account_id: target_user.id,
1642 comment: "I don't like this user"
1645 CommonAPI.update_report_state(second_report_id, "closed")
1649 |> get("/api/pleroma/admin/reports", %{
1652 |> json_response(:ok)
1654 [open_report] = response["reports"]
1656 assert length(response["reports"]) == 1
1657 assert open_report["id"] == first_report_id
1659 assert response["total"] == 1
1663 |> get("/api/pleroma/admin/reports", %{
1666 |> json_response(:ok)
1668 [closed_report] = response["reports"]
1670 assert length(response["reports"]) == 1
1671 assert closed_report["id"] == second_report_id
1673 assert response["total"] == 1
1677 |> get("/api/pleroma/admin/reports", %{
1678 "state" => "resolved"
1680 |> json_response(:ok)
1682 assert Enum.empty?(response["reports"])
1683 assert response["total"] == 0
1686 test "returns 403 when requested by a non-admin" do
1687 user = insert(:user)
1688 token = insert(:oauth_token, user: user)
1692 |> assign(:user, user)
1693 |> assign(:token, token)
1694 |> get("/api/pleroma/admin/reports")
1696 assert json_response(conn, :forbidden) ==
1697 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1700 test "returns 403 when requested by anonymous" do
1701 conn = get(build_conn(), "/api/pleroma/admin/reports")
1703 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1707 describe "GET /api/pleroma/admin/config" do
1708 setup do: clear_config(:configurable_from_database, true)
1710 test "when configuration from database is off", %{conn: conn} do
1711 Config.put(:configurable_from_database, false)
1712 conn = get(conn, "/api/pleroma/admin/config")
1714 assert json_response(conn, 400) ==
1716 "error" => "To use this endpoint you need to enable configuration from database."
1720 test "with settings only in db", %{conn: conn} do
1721 config1 = insert(:config)
1722 config2 = insert(:config)
1724 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1729 "group" => ":pleroma",
1734 "group" => ":pleroma",
1739 } = json_response(conn, 200)
1741 assert key1 == config1.key
1742 assert key2 == config2.key
1745 test "db is added to settings that are in db", %{conn: conn} do
1746 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1748 %{"configs" => configs} =
1750 |> get("/api/pleroma/admin/config")
1751 |> json_response(200)
1754 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1755 group == ":pleroma" and key == ":instance"
1758 assert instance_config["db"] == [":name"]
1761 test "merged default setting with db settings", %{conn: conn} do
1762 config1 = insert(:config)
1763 config2 = insert(:config)
1767 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1770 %{"configs" => configs} =
1772 |> get("/api/pleroma/admin/config")
1773 |> json_response(200)
1775 assert length(configs) > 3
1778 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1779 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1782 assert length(received_configs) == 3
1786 |> ConfigDB.from_binary()
1788 |> ConfigDB.convert()
1790 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1791 assert db in [[config1.key], [config2.key], db_keys]
1794 ConfigDB.from_binary_with_convert(config1.value),
1795 ConfigDB.from_binary_with_convert(config2.value),
1796 ConfigDB.from_binary_with_convert(config3.value)
1801 test "subkeys with full update right merge", %{conn: conn} do
1805 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1811 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1814 %{"configs" => configs} =
1816 |> get("/api/pleroma/admin/config")
1817 |> json_response(200)
1820 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1821 group == ":pleroma" and key in [config1.key, config2.key]
1824 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
1825 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
1827 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
1828 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
1830 assert emoji_val[:groups] == [a: 1, b: 2]
1831 assert assets_val[:mascots] == [a: 1, b: 2]
1835 test "POST /api/pleroma/admin/config error", %{conn: conn} do
1836 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
1838 assert json_response(conn, 400) ==
1839 %{"error" => "To use this endpoint you need to enable configuration from database."}
1842 describe "POST /api/pleroma/admin/config" do
1844 http = Application.get_env(:pleroma, :http)
1847 Application.delete_env(:pleroma, :key1)
1848 Application.delete_env(:pleroma, :key2)
1849 Application.delete_env(:pleroma, :key3)
1850 Application.delete_env(:pleroma, :key4)
1851 Application.delete_env(:pleroma, :keyaa1)
1852 Application.delete_env(:pleroma, :keyaa2)
1853 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1854 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1855 Application.put_env(:pleroma, :http, http)
1856 Application.put_env(:tesla, :adapter, Tesla.Mock)
1857 Restarter.Pleroma.refresh()
1861 setup do: clear_config(:configurable_from_database, true)
1863 @tag capture_log: true
1864 test "create new config setting in db", %{conn: conn} do
1865 ueberauth = Application.get_env(:ueberauth, Ueberauth)
1866 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
1869 post(conn, "/api/pleroma/admin/config", %{
1871 %{group: ":pleroma", key: ":key1", value: "value1"},
1873 group: ":ueberauth",
1875 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1881 ":nested_1" => "nested_value1",
1883 %{":nested_22" => "nested_value222"},
1884 %{":nested_33" => %{":nested_44" => "nested_444"}}
1892 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1893 %{"nested_4" => true}
1899 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
1904 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
1909 assert json_response(conn, 200) == %{
1912 "group" => ":pleroma",
1914 "value" => "value1",
1918 "group" => ":ueberauth",
1919 "key" => "Ueberauth",
1920 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
1921 "db" => [":consumer_secret"]
1924 "group" => ":pleroma",
1927 ":nested_1" => "nested_value1",
1929 %{":nested_22" => "nested_value222"},
1930 %{":nested_33" => %{":nested_44" => "nested_444"}}
1936 "group" => ":pleroma",
1939 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1940 %{"nested_4" => true}
1945 "group" => ":pleroma",
1947 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
1953 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
1959 assert Application.get_env(:pleroma, :key1) == "value1"
1961 assert Application.get_env(:pleroma, :key2) == %{
1962 nested_1: "nested_value1",
1964 %{nested_22: "nested_value222"},
1965 %{nested_33: %{nested_44: "nested_444"}}
1969 assert Application.get_env(:pleroma, :key3) == [
1970 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
1971 %{"nested_4" => true}
1974 assert Application.get_env(:pleroma, :key4) == %{
1975 "endpoint" => "https://example.com",
1979 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
1982 test "save configs setting without explicit key", %{conn: conn} do
1983 level = Application.get_env(:quack, :level)
1984 meta = Application.get_env(:quack, :meta)
1985 webhook_url = Application.get_env(:quack, :webhook_url)
1988 Application.put_env(:quack, :level, level)
1989 Application.put_env(:quack, :meta, meta)
1990 Application.put_env(:quack, :webhook_url, webhook_url)
1994 post(conn, "/api/pleroma/admin/config", %{
2008 key: ":webhook_url",
2009 value: "https://hooks.slack.com/services/KEY"
2014 assert json_response(conn, 200) == %{
2017 "group" => ":quack",
2023 "group" => ":quack",
2025 "value" => [":none"],
2029 "group" => ":quack",
2030 "key" => ":webhook_url",
2031 "value" => "https://hooks.slack.com/services/KEY",
2032 "db" => [":webhook_url"]
2037 assert Application.get_env(:quack, :level) == :info
2038 assert Application.get_env(:quack, :meta) == [:none]
2039 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
2042 test "saving config with partial update", %{conn: conn} do
2043 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
2046 post(conn, "/api/pleroma/admin/config", %{
2048 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
2052 assert json_response(conn, 200) == %{
2055 "group" => ":pleroma",
2058 %{"tuple" => [":key1", 1]},
2059 %{"tuple" => [":key2", 2]},
2060 %{"tuple" => [":key3", 3]}
2062 "db" => [":key1", ":key2", ":key3"]
2068 test "saving config which need pleroma reboot", %{conn: conn} do
2069 chat = Config.get(:chat)
2070 on_exit(fn -> Config.put(:chat, chat) end)
2074 "/api/pleroma/admin/config",
2077 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2081 |> json_response(200) == %{
2084 "db" => [":enabled"],
2085 "group" => ":pleroma",
2087 "value" => [%{"tuple" => [":enabled", true]}]
2090 "need_reboot" => true
2095 |> get("/api/pleroma/admin/config")
2096 |> json_response(200)
2098 assert configs["need_reboot"]
2101 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2102 end) =~ "pleroma restarted"
2106 |> get("/api/pleroma/admin/config")
2107 |> json_response(200)
2109 assert configs["need_reboot"] == false
2112 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
2113 chat = Config.get(:chat)
2114 on_exit(fn -> Config.put(:chat, chat) end)
2118 "/api/pleroma/admin/config",
2121 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2125 |> json_response(200) == %{
2128 "db" => [":enabled"],
2129 "group" => ":pleroma",
2131 "value" => [%{"tuple" => [":enabled", true]}]
2134 "need_reboot" => true
2137 assert post(conn, "/api/pleroma/admin/config", %{
2139 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
2142 |> json_response(200) == %{
2145 "group" => ":pleroma",
2148 %{"tuple" => [":key3", 3]}
2153 "need_reboot" => true
2157 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2158 end) =~ "pleroma restarted"
2162 |> get("/api/pleroma/admin/config")
2163 |> json_response(200)
2165 assert configs["need_reboot"] == false
2168 test "saving config with nested merge", %{conn: conn} do
2170 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
2173 post(conn, "/api/pleroma/admin/config", %{
2176 group: config.group,
2179 %{"tuple" => [":key3", 3]},
2184 %{"tuple" => [":k2", 1]},
2185 %{"tuple" => [":k3", 3]}
2194 assert json_response(conn, 200) == %{
2197 "group" => ":pleroma",
2200 %{"tuple" => [":key1", 1]},
2201 %{"tuple" => [":key3", 3]},
2206 %{"tuple" => [":k1", 1]},
2207 %{"tuple" => [":k2", 1]},
2208 %{"tuple" => [":k3", 3]}
2213 "db" => [":key1", ":key3", ":key2"]
2219 test "saving special atoms", %{conn: conn} do
2221 post(conn, "/api/pleroma/admin/config", %{
2224 "group" => ":pleroma",
2230 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2238 assert json_response(conn, 200) == %{
2241 "group" => ":pleroma",
2247 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2251 "db" => [":ssl_options"]
2256 assert Application.get_env(:pleroma, :key1) == [
2257 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
2261 test "saving full setting if value is in full_key_update list", %{conn: conn} do
2262 backends = Application.get_env(:logger, :backends)
2263 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
2269 value: :erlang.term_to_binary([])
2272 Pleroma.Config.TransferTask.load_and_update_env([], false)
2274 assert Application.get_env(:logger, :backends) == []
2277 post(conn, "/api/pleroma/admin/config", %{
2280 group: config.group,
2287 assert json_response(conn, 200) == %{
2290 "group" => ":logger",
2291 "key" => ":backends",
2295 "db" => [":backends"]
2300 assert Application.get_env(:logger, :backends) == [
2305 test "saving full setting if value is not keyword", %{conn: conn} do
2310 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
2314 post(conn, "/api/pleroma/admin/config", %{
2316 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
2320 assert json_response(conn, 200) == %{
2323 "group" => ":tesla",
2324 "key" => ":adapter",
2325 "value" => "Tesla.Adapter.Httpc",
2326 "db" => [":adapter"]
2332 test "update config setting & delete with fallback to default value", %{
2337 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2338 config1 = insert(:config, key: ":keyaa1")
2339 config2 = insert(:config, key: ":keyaa2")
2343 group: ":ueberauth",
2348 post(conn, "/api/pleroma/admin/config", %{
2350 %{group: config1.group, key: config1.key, value: "another_value"},
2351 %{group: config2.group, key: config2.key, value: "another_value"}
2355 assert json_response(conn, 200) == %{
2358 "group" => ":pleroma",
2359 "key" => config1.key,
2360 "value" => "another_value",
2364 "group" => ":pleroma",
2365 "key" => config2.key,
2366 "value" => "another_value",
2372 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2373 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
2374 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
2378 |> assign(:user, admin)
2379 |> assign(:token, token)
2380 |> post("/api/pleroma/admin/config", %{
2382 %{group: config2.group, key: config2.key, delete: true},
2384 group: ":ueberauth",
2391 assert json_response(conn, 200) == %{
2395 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
2396 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
2399 test "common config example", %{conn: conn} do
2401 post(conn, "/api/pleroma/admin/config", %{
2404 "group" => ":pleroma",
2405 "key" => "Pleroma.Captcha.NotReal",
2407 %{"tuple" => [":enabled", false]},
2408 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2409 %{"tuple" => [":seconds_valid", 60]},
2410 %{"tuple" => [":path", ""]},
2411 %{"tuple" => [":key1", nil]},
2412 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2413 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2414 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2415 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2416 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2417 %{"tuple" => [":name", "Pleroma"]}
2423 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2425 assert json_response(conn, 200) == %{
2428 "group" => ":pleroma",
2429 "key" => "Pleroma.Captcha.NotReal",
2431 %{"tuple" => [":enabled", false]},
2432 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2433 %{"tuple" => [":seconds_valid", 60]},
2434 %{"tuple" => [":path", ""]},
2435 %{"tuple" => [":key1", nil]},
2436 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2437 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2438 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2439 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2440 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2441 %{"tuple" => [":name", "Pleroma"]}
2461 test "tuples with more than two values", %{conn: conn} do
2463 post(conn, "/api/pleroma/admin/config", %{
2466 "group" => ":pleroma",
2467 "key" => "Pleroma.Web.Endpoint.NotReal",
2483 "/api/v1/streaming",
2484 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2491 "Phoenix.Endpoint.CowboyWebSocket",
2494 "Phoenix.Transports.WebSocket",
2497 "Pleroma.Web.Endpoint",
2498 "Pleroma.Web.UserSocket",
2509 "Phoenix.Endpoint.Cowboy2Handler",
2510 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2527 assert json_response(conn, 200) == %{
2530 "group" => ":pleroma",
2531 "key" => "Pleroma.Web.Endpoint.NotReal",
2547 "/api/v1/streaming",
2548 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2555 "Phoenix.Endpoint.CowboyWebSocket",
2558 "Phoenix.Transports.WebSocket",
2561 "Pleroma.Web.Endpoint",
2562 "Pleroma.Web.UserSocket",
2573 "Phoenix.Endpoint.Cowboy2Handler",
2574 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2593 test "settings with nesting map", %{conn: conn} do
2595 post(conn, "/api/pleroma/admin/config", %{
2598 "group" => ":pleroma",
2601 %{"tuple" => [":key2", "some_val"]},
2606 ":max_options" => 20,
2607 ":max_option_chars" => 200,
2608 ":min_expiration" => 0,
2609 ":max_expiration" => 31_536_000,
2611 ":max_options" => 20,
2612 ":max_option_chars" => 200,
2613 ":min_expiration" => 0,
2614 ":max_expiration" => 31_536_000
2624 assert json_response(conn, 200) ==
2628 "group" => ":pleroma",
2631 %{"tuple" => [":key2", "some_val"]},
2636 ":max_expiration" => 31_536_000,
2637 ":max_option_chars" => 200,
2638 ":max_options" => 20,
2639 ":min_expiration" => 0,
2641 ":max_expiration" => 31_536_000,
2642 ":max_option_chars" => 200,
2643 ":max_options" => 20,
2644 ":min_expiration" => 0
2650 "db" => [":key2", ":key3"]
2656 test "value as map", %{conn: conn} do
2658 post(conn, "/api/pleroma/admin/config", %{
2661 "group" => ":pleroma",
2663 "value" => %{"key" => "some_val"}
2668 assert json_response(conn, 200) ==
2672 "group" => ":pleroma",
2674 "value" => %{"key" => "some_val"},
2681 test "queues key as atom", %{conn: conn} do
2683 post(conn, "/api/pleroma/admin/config", %{
2689 %{"tuple" => [":federator_incoming", 50]},
2690 %{"tuple" => [":federator_outgoing", 50]},
2691 %{"tuple" => [":web_push", 50]},
2692 %{"tuple" => [":mailer", 10]},
2693 %{"tuple" => [":transmogrifier", 20]},
2694 %{"tuple" => [":scheduled_activities", 10]},
2695 %{"tuple" => [":background", 5]}
2701 assert json_response(conn, 200) == %{
2707 %{"tuple" => [":federator_incoming", 50]},
2708 %{"tuple" => [":federator_outgoing", 50]},
2709 %{"tuple" => [":web_push", 50]},
2710 %{"tuple" => [":mailer", 10]},
2711 %{"tuple" => [":transmogrifier", 20]},
2712 %{"tuple" => [":scheduled_activities", 10]},
2713 %{"tuple" => [":background", 5]}
2716 ":federator_incoming",
2717 ":federator_outgoing",
2721 ":scheduled_activities",
2729 test "delete part of settings by atom subkeys", %{conn: conn} do
2733 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2737 post(conn, "/api/pleroma/admin/config", %{
2740 group: config.group,
2742 subkeys: [":subkey1", ":subkey3"],
2748 assert json_response(conn, 200) == %{
2751 "group" => ":pleroma",
2753 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2754 "db" => [":subkey2"]
2760 test "proxy tuple localhost", %{conn: conn} do
2762 post(conn, "/api/pleroma/admin/config", %{
2768 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
2777 "group" => ":pleroma",
2783 } = json_response(conn, 200)
2785 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
2786 assert ":proxy_url" in db
2789 test "proxy tuple domain", %{conn: conn} do
2791 post(conn, "/api/pleroma/admin/config", %{
2797 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
2806 "group" => ":pleroma",
2812 } = json_response(conn, 200)
2814 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
2815 assert ":proxy_url" in db
2818 test "proxy tuple ip", %{conn: conn} do
2820 post(conn, "/api/pleroma/admin/config", %{
2826 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
2835 "group" => ":pleroma",
2841 } = json_response(conn, 200)
2843 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
2844 assert ":proxy_url" in db
2847 @tag capture_log: true
2848 test "doesn't set keys not in the whitelist", %{conn: conn} do
2849 clear_config(:database_config_whitelist, [
2852 {:pleroma, Pleroma.Captcha.NotReal},
2856 post(conn, "/api/pleroma/admin/config", %{
2858 %{group: ":pleroma", key: ":key1", value: "value1"},
2859 %{group: ":pleroma", key: ":key2", value: "value2"},
2860 %{group: ":pleroma", key: ":key3", value: "value3"},
2861 %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
2862 %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
2863 %{group: ":not_real", key: ":anything", value: "value6"}
2867 assert Application.get_env(:pleroma, :key1) == "value1"
2868 assert Application.get_env(:pleroma, :key2) == "value2"
2869 assert Application.get_env(:pleroma, :key3) == nil
2870 assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
2871 assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
2872 assert Application.get_env(:not_real, :anything) == "value6"
2876 describe "GET /api/pleroma/admin/restart" do
2877 setup do: clear_config(:configurable_from_database, true)
2879 test "pleroma restarts", %{conn: conn} do
2881 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2882 end) =~ "pleroma restarted"
2884 refute Restarter.Pleroma.need_reboot?()
2888 test "need_reboot flag", %{conn: conn} do
2890 |> get("/api/pleroma/admin/need_reboot")
2891 |> json_response(200) == %{"need_reboot" => false}
2893 Restarter.Pleroma.need_reboot()
2896 |> get("/api/pleroma/admin/need_reboot")
2897 |> json_response(200) == %{"need_reboot" => true}
2899 on_exit(fn -> Restarter.Pleroma.refresh() end)
2902 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2904 user = insert(:user)
2906 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2907 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2908 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2910 insert(:note_activity, user: user, published: date1)
2911 insert(:note_activity, user: user, published: date2)
2912 insert(:note_activity, user: user, published: date3)
2917 test "renders user's statuses", %{conn: conn, user: user} do
2918 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2920 assert json_response(conn, 200) |> length() == 3
2923 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2924 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2926 assert json_response(conn, 200) |> length() == 2
2929 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2930 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
2932 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
2934 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2936 assert json_response(conn, 200) |> length() == 4
2939 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2940 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
2942 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
2944 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2946 assert json_response(conn, 200) |> length() == 5
2949 test "excludes reblogs by default", %{conn: conn, user: user} do
2950 other_user = insert(:user)
2951 {:ok, activity} = CommonAPI.post(user, %{status: "."})
2952 {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
2954 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
2955 assert json_response(conn_res, 200) |> length() == 0
2958 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
2960 assert json_response(conn_res, 200) |> length() == 1
2964 describe "GET /api/pleroma/admin/moderation_log" do
2966 moderator = insert(:user, is_moderator: true)
2968 %{moderator: moderator}
2971 test "returns the log", %{conn: conn, admin: admin} do
2972 Repo.insert(%ModerationLog{
2976 "nickname" => admin.nickname,
2979 action: "relay_follow",
2980 target: "https://example.org/relay"
2982 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2985 Repo.insert(%ModerationLog{
2989 "nickname" => admin.nickname,
2992 action: "relay_unfollow",
2993 target: "https://example.org/relay"
2995 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2998 conn = get(conn, "/api/pleroma/admin/moderation_log")
3000 response = json_response(conn, 200)
3001 [first_entry, second_entry] = response["items"]
3003 assert response["total"] == 2
3004 assert first_entry["data"]["action"] == "relay_unfollow"
3006 assert first_entry["message"] ==
3007 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3009 assert second_entry["data"]["action"] == "relay_follow"
3011 assert second_entry["message"] ==
3012 "@#{admin.nickname} followed relay: https://example.org/relay"
3015 test "returns the log with pagination", %{conn: conn, admin: admin} do
3016 Repo.insert(%ModerationLog{
3020 "nickname" => admin.nickname,
3023 action: "relay_follow",
3024 target: "https://example.org/relay"
3026 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3029 Repo.insert(%ModerationLog{
3033 "nickname" => admin.nickname,
3036 action: "relay_unfollow",
3037 target: "https://example.org/relay"
3039 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3042 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3044 response1 = json_response(conn1, 200)
3045 [first_entry] = response1["items"]
3047 assert response1["total"] == 2
3048 assert response1["items"] |> length() == 1
3049 assert first_entry["data"]["action"] == "relay_unfollow"
3051 assert first_entry["message"] ==
3052 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3054 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3056 response2 = json_response(conn2, 200)
3057 [second_entry] = response2["items"]
3059 assert response2["total"] == 2
3060 assert response2["items"] |> length() == 1
3061 assert second_entry["data"]["action"] == "relay_follow"
3063 assert second_entry["message"] ==
3064 "@#{admin.nickname} followed relay: https://example.org/relay"
3067 test "filters log by date", %{conn: conn, admin: admin} do
3068 first_date = "2017-08-15T15:47:06Z"
3069 second_date = "2017-08-20T15:47:06Z"
3071 Repo.insert(%ModerationLog{
3075 "nickname" => admin.nickname,
3078 action: "relay_follow",
3079 target: "https://example.org/relay"
3081 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3084 Repo.insert(%ModerationLog{
3088 "nickname" => admin.nickname,
3091 action: "relay_unfollow",
3092 target: "https://example.org/relay"
3094 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3100 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3103 response1 = json_response(conn1, 200)
3104 [first_entry] = response1["items"]
3106 assert response1["total"] == 1
3107 assert first_entry["data"]["action"] == "relay_unfollow"
3109 assert first_entry["message"] ==
3110 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3113 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3114 Repo.insert(%ModerationLog{
3118 "nickname" => admin.nickname,
3121 action: "relay_follow",
3122 target: "https://example.org/relay"
3126 Repo.insert(%ModerationLog{
3129 "id" => moderator.id,
3130 "nickname" => moderator.nickname,
3133 action: "relay_unfollow",
3134 target: "https://example.org/relay"
3138 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3140 response1 = json_response(conn1, 200)
3141 [first_entry] = response1["items"]
3143 assert response1["total"] == 1
3144 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3147 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3148 ModerationLog.insert_log(%{
3150 action: "relay_follow",
3151 target: "https://example.org/relay"
3154 ModerationLog.insert_log(%{
3156 action: "relay_unfollow",
3157 target: "https://example.org/relay"
3160 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3162 response1 = json_response(conn1, 200)
3163 [first_entry] = response1["items"]
3165 assert response1["total"] == 1
3167 assert get_in(first_entry, ["data", "message"]) ==
3168 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3172 describe "GET /users/:nickname/credentials" do
3173 test "gets the user credentials", %{conn: conn} do
3174 user = insert(:user)
3175 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3177 response = assert json_response(conn, 200)
3178 assert response["email"] == user.email
3181 test "returns 403 if requested by a non-admin" do
3182 user = insert(:user)
3186 |> assign(:user, user)
3187 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3189 assert json_response(conn, :forbidden)
3193 describe "PATCH /users/:nickname/credentials" do
3195 user = insert(:user)
3199 test "changes password and email", %{conn: conn, admin: admin, user: user} do
3200 assert user.password_reset_pending == false
3203 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3204 "password" => "new_password",
3205 "email" => "new_email@example.com",
3206 "name" => "new_name"
3209 assert json_response(conn, 200) == %{"status" => "success"}
3211 ObanHelpers.perform_all()
3213 updated_user = User.get_by_id(user.id)
3215 assert updated_user.email == "new_email@example.com"
3216 assert updated_user.name == "new_name"
3217 assert updated_user.password_hash != user.password_hash
3218 assert updated_user.password_reset_pending == true
3220 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3222 assert ModerationLog.get_log_entry_message(log_entry1) ==
3223 "@#{admin.nickname} updated users: @#{user.nickname}"
3225 assert ModerationLog.get_log_entry_message(log_entry2) ==
3226 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3229 test "returns 403 if requested by a non-admin", %{user: user} do
3232 |> assign(:user, user)
3233 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3234 "password" => "new_password",
3235 "email" => "new_email@example.com",
3236 "name" => "new_name"
3239 assert json_response(conn, :forbidden)
3242 test "changes actor type from permitted list", %{conn: conn, user: user} do
3243 assert user.actor_type == "Person"
3245 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3246 "actor_type" => "Service"
3248 |> json_response(200) == %{"status" => "success"}
3250 updated_user = User.get_by_id(user.id)
3252 assert updated_user.actor_type == "Service"
3254 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3255 "actor_type" => "Application"
3257 |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
3260 test "update non existing user", %{conn: conn} do
3261 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
3262 "password" => "new_password"
3264 |> json_response(200) == %{"error" => "Unable to update user."}
3268 describe "PATCH /users/:nickname/force_password_reset" do
3269 test "sets password_reset_pending to true", %{conn: conn} do
3270 user = insert(:user)
3271 assert user.password_reset_pending == false
3274 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3276 assert json_response(conn, 204) == ""
3278 ObanHelpers.perform_all()
3280 assert User.get_by_id(user.id).password_reset_pending == true
3284 describe "relays" do
3285 test "POST /relay", %{conn: conn, admin: admin} do
3287 post(conn, "/api/pleroma/admin/relay", %{
3288 relay_url: "http://mastodon.example.org/users/admin"
3291 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3293 log_entry = Repo.one(ModerationLog)
3295 assert ModerationLog.get_log_entry_message(log_entry) ==
3296 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3299 test "GET /relay", %{conn: conn} do
3300 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3302 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3303 |> Enum.each(fn ap_id ->
3304 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3305 User.follow(relay_user, user)
3308 conn = get(conn, "/api/pleroma/admin/relay")
3310 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3313 test "DELETE /relay", %{conn: conn, admin: admin} do
3314 post(conn, "/api/pleroma/admin/relay", %{
3315 relay_url: "http://mastodon.example.org/users/admin"
3319 delete(conn, "/api/pleroma/admin/relay", %{
3320 relay_url: "http://mastodon.example.org/users/admin"
3323 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3325 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3327 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3328 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3330 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3331 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3335 describe "instances" do
3336 test "GET /instances/:instance/statuses", %{conn: conn} do
3337 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3338 user2 = insert(:user, local: false, nickname: "test@test.com")
3339 insert_pair(:note_activity, user: user)
3340 activity = insert(:note_activity, user: user2)
3342 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3344 response = json_response(ret_conn, 200)
3346 assert length(response) == 2
3348 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3350 response = json_response(ret_conn, 200)
3352 assert length(response) == 1
3354 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3356 response = json_response(ret_conn, 200)
3358 assert Enum.empty?(response)
3360 CommonAPI.repeat(activity.id, user)
3362 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3363 response = json_response(ret_conn, 200)
3364 assert length(response) == 2
3366 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3367 response = json_response(ret_conn, 200)
3368 assert length(response) == 3
3372 describe "PATCH /confirm_email" do
3373 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3374 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3376 assert first_user.confirmation_pending == true
3377 assert second_user.confirmation_pending == true
3380 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3382 first_user.nickname,
3383 second_user.nickname
3387 assert ret_conn.status == 200
3389 assert first_user.confirmation_pending == true
3390 assert second_user.confirmation_pending == true
3392 log_entry = Repo.one(ModerationLog)
3394 assert ModerationLog.get_log_entry_message(log_entry) ==
3395 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3396 second_user.nickname
3401 describe "PATCH /resend_confirmation_email" do
3402 test "it resend emails for two users", %{conn: conn, admin: admin} do
3403 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3406 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3408 first_user.nickname,
3409 second_user.nickname
3413 assert ret_conn.status == 200
3415 log_entry = Repo.one(ModerationLog)
3417 assert ModerationLog.get_log_entry_message(log_entry) ==
3418 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3419 second_user.nickname
3424 describe "POST /reports/:id/notes" do
3425 setup %{conn: conn, admin: admin} do
3426 [reporter, target_user] = insert_pair(:user)
3427 activity = insert(:note_activity, user: target_user)
3429 {:ok, %{id: report_id}} =
3430 CommonAPI.report(reporter, %{
3431 account_id: target_user.id,
3432 comment: "I feel offended",
3433 status_ids: [activity.id]
3436 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3437 content: "this is disgusting!"
3440 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3441 content: "this is disgusting2!"
3446 report_id: report_id
3450 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3451 [note, _] = Repo.all(ReportNote)
3454 activity_id: ^report_id,
3455 content: "this is disgusting!",
3460 test "it returns reports with notes", %{conn: conn, admin: admin} do
3461 conn = get(conn, "/api/pleroma/admin/reports")
3463 response = json_response(conn, 200)
3464 notes = hd(response["reports"])["notes"]
3467 assert note["user"]["nickname"] == admin.nickname
3468 assert note["content"] == "this is disgusting!"
3469 assert note["created_at"]
3470 assert response["total"] == 1
3473 test "it deletes the note", %{conn: conn, report_id: report_id} do
3474 assert ReportNote |> Repo.all() |> length() == 2
3476 [note, _] = Repo.all(ReportNote)
3478 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3480 assert ReportNote |> Repo.all() |> length() == 1
3484 describe "GET /api/pleroma/admin/config/descriptions" do
3485 test "structure", %{conn: conn} do
3486 admin = insert(:user, is_admin: true)
3489 assign(conn, :user, admin)
3490 |> get("/api/pleroma/admin/config/descriptions")
3492 assert [child | _others] = json_response(conn, 200)
3494 assert child["children"]
3496 assert String.starts_with?(child["group"], ":")
3497 assert child["description"]
3500 test "filters by database configuration whitelist", %{conn: conn} do
3501 clear_config(:database_config_whitelist, [
3502 {:pleroma, :instance},
3503 {:pleroma, :activitypub},
3504 {:pleroma, Pleroma.Upload},
3508 admin = insert(:user, is_admin: true)
3511 assign(conn, :user, admin)
3512 |> get("/api/pleroma/admin/config/descriptions")
3514 children = json_response(conn, 200)
3516 assert length(children) == 4
3518 assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
3520 instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
3521 assert instance["children"]
3523 activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
3524 assert activitypub["children"]
3526 web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
3527 assert web_endpoint["children"]
3529 esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
3530 assert esshd["children"]
3534 describe "/api/pleroma/admin/stats" do
3535 test "status visibility count", %{conn: conn} do
3536 admin = insert(:user, is_admin: true)
3537 user = insert(:user)
3538 CommonAPI.post(user, %{visibility: "public", status: "hey"})
3539 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3540 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3544 |> assign(:user, admin)
3545 |> get("/api/pleroma/admin/stats")
3546 |> json_response(200)
3548 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3549 response["status_visibility"]
3553 describe "POST /api/pleroma/admin/oauth_app" do
3554 test "errors", %{conn: conn} do
3555 response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
3557 assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
3560 test "success", %{conn: conn} do
3561 base_url = Web.base_url()
3562 app_name = "Trusted app"
3566 |> post("/api/pleroma/admin/oauth_app", %{
3568 redirect_uris: base_url
3570 |> json_response(200)
3574 "client_secret" => _,
3575 "name" => ^app_name,
3576 "redirect_uri" => ^base_url,
3581 test "with trusted", %{conn: conn} do
3582 base_url = Web.base_url()
3583 app_name = "Trusted app"
3587 |> post("/api/pleroma/admin/oauth_app", %{
3589 redirect_uris: base_url,
3592 |> json_response(200)
3596 "client_secret" => _,
3597 "name" => ^app_name,
3598 "redirect_uri" => ^base_url,
3604 describe "GET /api/pleroma/admin/oauth_app" do
3606 app = insert(:oauth_app)
3610 test "list", %{conn: conn} do
3613 |> get("/api/pleroma/admin/oauth_app")
3614 |> json_response(200)
3616 assert %{"apps" => apps, "count" => count, "page_size" => _} = response
3618 assert length(apps) == count
3621 test "with page size", %{conn: conn} do
3627 |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
3628 |> json_response(200)
3630 assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
3632 assert length(apps) == page_size
3635 test "search by client name", %{conn: conn, app: app} do
3638 |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
3639 |> json_response(200)
3641 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3643 assert returned["client_id"] == app.client_id
3644 assert returned["name"] == app.client_name
3647 test "search by client id", %{conn: conn, app: app} do
3650 |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
3651 |> json_response(200)
3653 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3655 assert returned["client_id"] == app.client_id
3656 assert returned["name"] == app.client_name
3659 test "only trusted", %{conn: conn} do
3660 app = insert(:oauth_app, trusted: true)
3664 |> get("/api/pleroma/admin/oauth_app", %{trusted: true})
3665 |> json_response(200)
3667 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3669 assert returned["client_id"] == app.client_id
3670 assert returned["name"] == app.client_name
3674 describe "DELETE /api/pleroma/admin/oauth_app/:id" do
3675 test "with id", %{conn: conn} do
3676 app = insert(:oauth_app)
3680 |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
3681 |> json_response(:no_content)
3683 assert response == ""
3686 test "with non existance id", %{conn: conn} do
3689 |> delete("/api/pleroma/admin/oauth_app/0")
3690 |> json_response(:bad_request)
3692 assert response == ""
3696 describe "PATCH /api/pleroma/admin/oauth_app/:id" do
3697 test "with id", %{conn: conn} do
3698 app = insert(:oauth_app)
3700 name = "another name"
3701 url = "https://example.com"
3704 website = "http://website.com"
3708 |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
3715 |> json_response(200)
3719 "client_secret" => _,
3722 "redirect_uri" => ^url,
3724 "website" => ^website
3728 test "without id", %{conn: conn} do
3731 |> patch("/api/pleroma/admin/oauth_app/0")
3732 |> json_response(:bad_request)
3734 assert response == ""
3739 # Needed for testing
3740 defmodule Pleroma.Web.Endpoint.NotReal do
3743 defmodule Pleroma.Captcha.NotReal do