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
3194 test "changes password and email", %{conn: conn, admin: admin} do
3195 user = insert(:user)
3196 assert user.password_reset_pending == false
3199 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3200 "password" => "new_password",
3201 "email" => "new_email@example.com",
3202 "name" => "new_name"
3205 assert json_response(conn, 200) == %{"status" => "success"}
3207 ObanHelpers.perform_all()
3209 updated_user = User.get_by_id(user.id)
3211 assert updated_user.email == "new_email@example.com"
3212 assert updated_user.name == "new_name"
3213 assert updated_user.password_hash != user.password_hash
3214 assert updated_user.password_reset_pending == true
3216 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3218 assert ModerationLog.get_log_entry_message(log_entry1) ==
3219 "@#{admin.nickname} updated users: @#{user.nickname}"
3221 assert ModerationLog.get_log_entry_message(log_entry2) ==
3222 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3225 test "returns 403 if requested by a non-admin" do
3226 user = insert(:user)
3230 |> assign(:user, user)
3231 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3232 "password" => "new_password",
3233 "email" => "new_email@example.com",
3234 "name" => "new_name"
3237 assert json_response(conn, :forbidden)
3241 describe "PATCH /users/:nickname/force_password_reset" do
3242 test "sets password_reset_pending to true", %{conn: conn} do
3243 user = insert(:user)
3244 assert user.password_reset_pending == false
3247 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3249 assert json_response(conn, 204) == ""
3251 ObanHelpers.perform_all()
3253 assert User.get_by_id(user.id).password_reset_pending == true
3257 describe "instances" do
3258 test "GET /instances/:instance/statuses", %{conn: conn} do
3259 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3260 user2 = insert(:user, local: false, nickname: "test@test.com")
3261 insert_pair(:note_activity, user: user)
3262 activity = insert(:note_activity, user: user2)
3264 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3266 response = json_response(ret_conn, 200)
3268 assert length(response) == 2
3270 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3272 response = json_response(ret_conn, 200)
3274 assert length(response) == 1
3276 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3278 response = json_response(ret_conn, 200)
3280 assert Enum.empty?(response)
3282 CommonAPI.repeat(activity.id, user)
3284 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3285 response = json_response(ret_conn, 200)
3286 assert length(response) == 2
3288 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3289 response = json_response(ret_conn, 200)
3290 assert length(response) == 3
3294 describe "PATCH /confirm_email" do
3295 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3296 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3298 assert first_user.confirmation_pending == true
3299 assert second_user.confirmation_pending == true
3302 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3304 first_user.nickname,
3305 second_user.nickname
3309 assert ret_conn.status == 200
3311 assert first_user.confirmation_pending == true
3312 assert second_user.confirmation_pending == true
3314 log_entry = Repo.one(ModerationLog)
3316 assert ModerationLog.get_log_entry_message(log_entry) ==
3317 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3318 second_user.nickname
3323 describe "PATCH /resend_confirmation_email" do
3324 test "it resend emails for two users", %{conn: conn, admin: admin} do
3325 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3328 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3330 first_user.nickname,
3331 second_user.nickname
3335 assert ret_conn.status == 200
3337 log_entry = Repo.one(ModerationLog)
3339 assert ModerationLog.get_log_entry_message(log_entry) ==
3340 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3341 second_user.nickname
3346 describe "POST /reports/:id/notes" do
3347 setup %{conn: conn, admin: admin} do
3348 [reporter, target_user] = insert_pair(:user)
3349 activity = insert(:note_activity, user: target_user)
3351 {:ok, %{id: report_id}} =
3352 CommonAPI.report(reporter, %{
3353 account_id: target_user.id,
3354 comment: "I feel offended",
3355 status_ids: [activity.id]
3358 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3359 content: "this is disgusting!"
3362 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3363 content: "this is disgusting2!"
3368 report_id: report_id
3372 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3373 [note, _] = Repo.all(ReportNote)
3376 activity_id: ^report_id,
3377 content: "this is disgusting!",
3382 test "it returns reports with notes", %{conn: conn, admin: admin} do
3383 conn = get(conn, "/api/pleroma/admin/reports")
3385 response = json_response(conn, 200)
3386 notes = hd(response["reports"])["notes"]
3389 assert note["user"]["nickname"] == admin.nickname
3390 assert note["content"] == "this is disgusting!"
3391 assert note["created_at"]
3392 assert response["total"] == 1
3395 test "it deletes the note", %{conn: conn, report_id: report_id} do
3396 assert ReportNote |> Repo.all() |> length() == 2
3398 [note, _] = Repo.all(ReportNote)
3400 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3402 assert ReportNote |> Repo.all() |> length() == 1
3406 describe "GET /api/pleroma/admin/config/descriptions" do
3407 test "structure", %{conn: conn} do
3408 admin = insert(:user, is_admin: true)
3411 assign(conn, :user, admin)
3412 |> get("/api/pleroma/admin/config/descriptions")
3414 assert [child | _others] = json_response(conn, 200)
3416 assert child["children"]
3418 assert String.starts_with?(child["group"], ":")
3419 assert child["description"]
3422 test "filters by database configuration whitelist", %{conn: conn} do
3423 clear_config(:database_config_whitelist, [
3424 {:pleroma, :instance},
3425 {:pleroma, :activitypub},
3426 {:pleroma, Pleroma.Upload},
3430 admin = insert(:user, is_admin: true)
3433 assign(conn, :user, admin)
3434 |> get("/api/pleroma/admin/config/descriptions")
3436 children = json_response(conn, 200)
3438 assert length(children) == 4
3440 assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
3442 instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
3443 assert instance["children"]
3445 activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
3446 assert activitypub["children"]
3448 web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
3449 assert web_endpoint["children"]
3451 esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
3452 assert esshd["children"]
3456 describe "/api/pleroma/admin/stats" do
3457 test "status visibility count", %{conn: conn} do
3458 admin = insert(:user, is_admin: true)
3459 user = insert(:user)
3460 CommonAPI.post(user, %{visibility: "public", status: "hey"})
3461 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3462 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3466 |> assign(:user, admin)
3467 |> get("/api/pleroma/admin/stats")
3468 |> json_response(200)
3470 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3471 response["status_visibility"]
3475 describe "POST /api/pleroma/admin/oauth_app" do
3476 test "errors", %{conn: conn} do
3477 response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
3479 assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
3482 test "success", %{conn: conn} do
3483 base_url = Web.base_url()
3484 app_name = "Trusted app"
3488 |> post("/api/pleroma/admin/oauth_app", %{
3490 redirect_uris: base_url
3492 |> json_response(200)
3496 "client_secret" => _,
3497 "name" => ^app_name,
3498 "redirect_uri" => ^base_url,
3503 test "with trusted", %{conn: conn} do
3504 base_url = Web.base_url()
3505 app_name = "Trusted app"
3509 |> post("/api/pleroma/admin/oauth_app", %{
3511 redirect_uris: base_url,
3514 |> json_response(200)
3518 "client_secret" => _,
3519 "name" => ^app_name,
3520 "redirect_uri" => ^base_url,
3526 describe "GET /api/pleroma/admin/oauth_app" do
3528 app = insert(:oauth_app)
3532 test "list", %{conn: conn} do
3535 |> get("/api/pleroma/admin/oauth_app")
3536 |> json_response(200)
3538 assert %{"apps" => apps, "count" => count, "page_size" => _} = response
3540 assert length(apps) == count
3543 test "with page size", %{conn: conn} do
3549 |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
3550 |> json_response(200)
3552 assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
3554 assert length(apps) == page_size
3557 test "search by client name", %{conn: conn, app: app} do
3560 |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
3561 |> json_response(200)
3563 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3565 assert returned["client_id"] == app.client_id
3566 assert returned["name"] == app.client_name
3569 test "search by client id", %{conn: conn, app: app} do
3572 |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
3573 |> json_response(200)
3575 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3577 assert returned["client_id"] == app.client_id
3578 assert returned["name"] == app.client_name
3581 test "only trusted", %{conn: conn} do
3582 app = insert(:oauth_app, trusted: true)
3586 |> get("/api/pleroma/admin/oauth_app", %{trusted: true})
3587 |> json_response(200)
3589 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3591 assert returned["client_id"] == app.client_id
3592 assert returned["name"] == app.client_name
3596 describe "DELETE /api/pleroma/admin/oauth_app/:id" do
3597 test "with id", %{conn: conn} do
3598 app = insert(:oauth_app)
3602 |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
3603 |> json_response(:no_content)
3605 assert response == ""
3608 test "with non existance id", %{conn: conn} do
3611 |> delete("/api/pleroma/admin/oauth_app/0")
3612 |> json_response(:bad_request)
3614 assert response == ""
3618 describe "PATCH /api/pleroma/admin/oauth_app/:id" do
3619 test "with id", %{conn: conn} do
3620 app = insert(:oauth_app)
3622 name = "another name"
3623 url = "https://example.com"
3626 website = "http://website.com"
3630 |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
3637 |> json_response(200)
3641 "client_secret" => _,
3644 "redirect_uri" => ^url,
3646 "website" => ^website
3650 test "without id", %{conn: conn} do
3653 |> patch("/api/pleroma/admin/oauth_app/0")
3654 |> json_response(:bad_request)
3656 assert response == ""
3661 # Needed for testing
3662 defmodule Pleroma.Web.Endpoint.NotReal do
3665 defmodule Pleroma.Captcha.NotReal do