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
152 with_mock Pleroma.Web.Federator,
153 publish: fn _ -> nil end do
156 |> put_req_header("accept", "application/json")
157 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
159 ObanHelpers.perform_all()
161 assert User.get_by_nickname(user.nickname).deactivated
163 log_entry = Repo.one(ModerationLog)
165 assert ModerationLog.get_log_entry_message(log_entry) ==
166 "@#{admin.nickname} deleted users: @#{user.nickname}"
168 assert json_response(conn, 200) == [user.nickname]
170 assert called(Pleroma.Web.Federator.publish(:_))
174 test "multiple users", %{admin: admin, conn: conn} do
175 user_one = insert(:user)
176 user_two = insert(:user)
180 |> put_req_header("accept", "application/json")
181 |> delete("/api/pleroma/admin/users", %{
182 nicknames: [user_one.nickname, user_two.nickname]
185 log_entry = Repo.one(ModerationLog)
187 assert ModerationLog.get_log_entry_message(log_entry) ==
188 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
190 response = json_response(conn, 200)
191 assert response -- [user_one.nickname, user_two.nickname] == []
195 describe "/api/pleroma/admin/users" do
196 test "Create", %{conn: conn} do
199 |> put_req_header("accept", "application/json")
200 |> post("/api/pleroma/admin/users", %{
203 "nickname" => "lain",
204 "email" => "lain@example.org",
208 "nickname" => "lain2",
209 "email" => "lain2@example.org",
215 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
216 assert response == ["success", "success"]
218 log_entry = Repo.one(ModerationLog)
220 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
223 test "Cannot create user with existing email", %{conn: conn} do
228 |> put_req_header("accept", "application/json")
229 |> post("/api/pleroma/admin/users", %{
232 "nickname" => "lain",
233 "email" => user.email,
239 assert json_response(conn, 409) == [
243 "email" => user.email,
246 "error" => "email has already been taken",
252 test "Cannot create user with existing nickname", %{conn: conn} do
257 |> put_req_header("accept", "application/json")
258 |> post("/api/pleroma/admin/users", %{
261 "nickname" => user.nickname,
262 "email" => "someuser@plerama.social",
268 assert json_response(conn, 409) == [
272 "email" => "someuser@plerama.social",
273 "nickname" => user.nickname
275 "error" => "nickname has already been taken",
281 test "Multiple user creation works in transaction", %{conn: conn} do
286 |> put_req_header("accept", "application/json")
287 |> post("/api/pleroma/admin/users", %{
290 "nickname" => "newuser",
291 "email" => "newuser@pleroma.social",
295 "nickname" => "lain",
296 "email" => user.email,
302 assert json_response(conn, 409) == [
306 "email" => user.email,
309 "error" => "email has already been taken",
315 "email" => "newuser@pleroma.social",
316 "nickname" => "newuser"
323 assert User.get_by_nickname("newuser") === nil
327 describe "/api/pleroma/admin/users/:nickname" do
328 test "Show", %{conn: conn} do
331 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
334 "deactivated" => false,
335 "id" => to_string(user.id),
337 "nickname" => user.nickname,
338 "roles" => %{"admin" => false, "moderator" => false},
340 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
341 "display_name" => HTML.strip_tags(user.name || user.nickname),
342 "confirmation_pending" => false
345 assert expected == json_response(conn, 200)
348 test "when the user doesn't exist", %{conn: conn} do
351 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
353 assert "Not found" == json_response(conn, 404)
357 describe "/api/pleroma/admin/users/follow" do
358 test "allows to force-follow another user", %{admin: admin, conn: conn} do
360 follower = insert(:user)
363 |> put_req_header("accept", "application/json")
364 |> post("/api/pleroma/admin/users/follow", %{
365 "follower" => follower.nickname,
366 "followed" => user.nickname
369 user = User.get_cached_by_id(user.id)
370 follower = User.get_cached_by_id(follower.id)
372 assert User.following?(follower, user)
374 log_entry = Repo.one(ModerationLog)
376 assert ModerationLog.get_log_entry_message(log_entry) ==
377 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
381 describe "/api/pleroma/admin/users/unfollow" do
382 test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
384 follower = insert(:user)
386 User.follow(follower, user)
389 |> put_req_header("accept", "application/json")
390 |> post("/api/pleroma/admin/users/unfollow", %{
391 "follower" => follower.nickname,
392 "followed" => user.nickname
395 user = User.get_cached_by_id(user.id)
396 follower = User.get_cached_by_id(follower.id)
398 refute User.following?(follower, user)
400 log_entry = Repo.one(ModerationLog)
402 assert ModerationLog.get_log_entry_message(log_entry) ==
403 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
407 describe "PUT /api/pleroma/admin/users/tag" do
408 setup %{conn: conn} do
409 user1 = insert(:user, %{tags: ["x"]})
410 user2 = insert(:user, %{tags: ["y"]})
411 user3 = insert(:user, %{tags: ["unchanged"]})
415 |> put_req_header("accept", "application/json")
417 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
418 "#{user2.nickname}&tags[]=foo&tags[]=bar"
421 %{conn: conn, user1: user1, user2: user2, user3: user3}
424 test "it appends specified tags to users with specified nicknames", %{
430 assert json_response(conn, :no_content)
431 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
432 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
434 log_entry = Repo.one(ModerationLog)
437 [user1.nickname, user2.nickname]
438 |> Enum.map(&"@#{&1}")
441 tags = ["foo", "bar"] |> Enum.join(", ")
443 assert ModerationLog.get_log_entry_message(log_entry) ==
444 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
447 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
448 assert json_response(conn, :no_content)
449 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
453 describe "DELETE /api/pleroma/admin/users/tag" do
454 setup %{conn: conn} do
455 user1 = insert(:user, %{tags: ["x"]})
456 user2 = insert(:user, %{tags: ["y", "z"]})
457 user3 = insert(:user, %{tags: ["unchanged"]})
461 |> put_req_header("accept", "application/json")
463 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
464 "#{user2.nickname}&tags[]=x&tags[]=z"
467 %{conn: conn, user1: user1, user2: user2, user3: user3}
470 test "it removes specified tags from users with specified nicknames", %{
476 assert json_response(conn, :no_content)
477 assert User.get_cached_by_id(user1.id).tags == []
478 assert User.get_cached_by_id(user2.id).tags == ["y"]
480 log_entry = Repo.one(ModerationLog)
483 [user1.nickname, user2.nickname]
484 |> Enum.map(&"@#{&1}")
487 tags = ["x", "z"] |> Enum.join(", ")
489 assert ModerationLog.get_log_entry_message(log_entry) ==
490 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
493 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
494 assert json_response(conn, :no_content)
495 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
499 describe "/api/pleroma/admin/users/:nickname/permission_group" do
500 test "GET is giving user_info", %{admin: admin, conn: conn} do
503 |> put_req_header("accept", "application/json")
504 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
506 assert json_response(conn, 200) == %{
508 "is_moderator" => false
512 test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
517 |> put_req_header("accept", "application/json")
518 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
520 assert json_response(conn, 200) == %{
524 log_entry = Repo.one(ModerationLog)
526 assert ModerationLog.get_log_entry_message(log_entry) ==
527 "@#{admin.nickname} made @#{user.nickname} admin"
530 test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
531 user_one = insert(:user)
532 user_two = insert(:user)
536 |> put_req_header("accept", "application/json")
537 |> post("/api/pleroma/admin/users/permission_group/admin", %{
538 nicknames: [user_one.nickname, user_two.nickname]
541 assert json_response(conn, 200) == %{"is_admin" => true}
543 log_entry = Repo.one(ModerationLog)
545 assert ModerationLog.get_log_entry_message(log_entry) ==
546 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
549 test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
550 user = insert(:user, is_admin: true)
554 |> put_req_header("accept", "application/json")
555 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
557 assert json_response(conn, 200) == %{"is_admin" => false}
559 log_entry = Repo.one(ModerationLog)
561 assert ModerationLog.get_log_entry_message(log_entry) ==
562 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
565 test "/:right DELETE, can remove from a permission group (multiple)", %{
569 user_one = insert(:user, is_admin: true)
570 user_two = insert(:user, is_admin: true)
574 |> put_req_header("accept", "application/json")
575 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
576 nicknames: [user_one.nickname, user_two.nickname]
579 assert json_response(conn, 200) == %{"is_admin" => false}
581 log_entry = Repo.one(ModerationLog)
583 assert ModerationLog.get_log_entry_message(log_entry) ==
584 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
590 describe "POST /api/pleroma/admin/email_invite, with valid config" do
591 setup do: clear_config([:instance, :registrations_open], false)
592 setup do: clear_config([:instance, :invites_enabled], true)
594 test "sends invitation and returns 204", %{admin: admin, conn: conn} do
595 recipient_email = "foo@bar.com"
596 recipient_name = "J. D."
601 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
604 assert json_response(conn, :no_content)
606 token_record = List.last(Repo.all(Pleroma.UserInviteToken))
608 refute token_record.used
610 notify_email = Config.get([:instance, :notify_email])
611 instance_name = Config.get([:instance, :name])
614 Pleroma.Emails.UserEmail.user_invitation_email(
621 Swoosh.TestAssertions.assert_email_sent(
622 from: {instance_name, notify_email},
623 to: {recipient_name, recipient_email},
624 html_body: email.html_body
628 test "it returns 403 if requested by a non-admin" do
629 non_admin_user = insert(:user)
630 token = insert(:oauth_token, user: non_admin_user)
634 |> assign(:user, non_admin_user)
635 |> assign(:token, token)
636 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
638 assert json_response(conn, :forbidden)
641 test "email with +", %{conn: conn, admin: admin} do
642 recipient_email = "foo+bar@baz.com"
645 |> put_req_header("content-type", "application/json;charset=utf-8")
646 |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
647 |> json_response(:no_content)
650 Pleroma.UserInviteToken
655 refute token_record.used
657 notify_email = Config.get([:instance, :notify_email])
658 instance_name = Config.get([:instance, :name])
661 Pleroma.Emails.UserEmail.user_invitation_email(
667 Swoosh.TestAssertions.assert_email_sent(
668 from: {instance_name, notify_email},
670 html_body: email.html_body
675 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
676 setup do: clear_config([:instance, :registrations_open])
677 setup do: clear_config([:instance, :invites_enabled])
679 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
680 Config.put([:instance, :registrations_open], false)
681 Config.put([:instance, :invites_enabled], false)
683 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
685 assert json_response(conn, :bad_request) ==
686 "To send invites you need to set the `invites_enabled` option to true."
689 test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
690 Config.put([:instance, :registrations_open], true)
691 Config.put([:instance, :invites_enabled], true)
693 conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
695 assert json_response(conn, :bad_request) ==
696 "To send invites you need to set the `registrations_open` option to false."
700 test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
705 |> put_req_header("accept", "application/json")
706 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
708 resp = json_response(conn, 200)
710 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
713 describe "GET /api/pleroma/admin/users" do
714 test "renders users array for the first page", %{conn: conn, admin: admin} do
715 user = insert(:user, local: false, tags: ["foo", "bar"])
716 conn = get(conn, "/api/pleroma/admin/users?page=1")
721 "deactivated" => admin.deactivated,
723 "nickname" => admin.nickname,
724 "roles" => %{"admin" => true, "moderator" => false},
727 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
728 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
729 "confirmation_pending" => false
732 "deactivated" => user.deactivated,
734 "nickname" => user.nickname,
735 "roles" => %{"admin" => false, "moderator" => false},
737 "tags" => ["foo", "bar"],
738 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
739 "display_name" => HTML.strip_tags(user.name || user.nickname),
740 "confirmation_pending" => false
743 |> Enum.sort_by(& &1["nickname"])
745 assert json_response(conn, 200) == %{
752 test "pagination works correctly with service users", %{conn: conn} do
753 service1 = insert(:user, ap_id: Web.base_url() <> "/relay")
754 service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch")
755 insert_list(25, :user)
757 assert %{"count" => 26, "page_size" => 10, "users" => users1} =
759 |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
760 |> json_response(200)
762 assert Enum.count(users1) == 10
763 assert service1 not in [users1]
764 assert service2 not in [users1]
766 assert %{"count" => 26, "page_size" => 10, "users" => users2} =
768 |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
769 |> json_response(200)
771 assert Enum.count(users2) == 10
772 assert service1 not in [users2]
773 assert service2 not in [users2]
775 assert %{"count" => 26, "page_size" => 10, "users" => users3} =
777 |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
778 |> json_response(200)
780 assert Enum.count(users3) == 6
781 assert service1 not in [users3]
782 assert service2 not in [users3]
785 test "renders empty array for the second page", %{conn: conn} do
788 conn = get(conn, "/api/pleroma/admin/users?page=2")
790 assert json_response(conn, 200) == %{
797 test "regular search", %{conn: conn} do
798 user = insert(:user, nickname: "bob")
800 conn = get(conn, "/api/pleroma/admin/users?query=bo")
802 assert json_response(conn, 200) == %{
807 "deactivated" => user.deactivated,
809 "nickname" => user.nickname,
810 "roles" => %{"admin" => false, "moderator" => false},
813 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
814 "display_name" => HTML.strip_tags(user.name || user.nickname),
815 "confirmation_pending" => false
821 test "search by domain", %{conn: conn} do
822 user = insert(:user, nickname: "nickname@domain.com")
825 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
827 assert json_response(conn, 200) == %{
832 "deactivated" => user.deactivated,
834 "nickname" => user.nickname,
835 "roles" => %{"admin" => false, "moderator" => false},
838 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
839 "display_name" => HTML.strip_tags(user.name || user.nickname),
840 "confirmation_pending" => false
846 test "search by full nickname", %{conn: conn} do
847 user = insert(:user, nickname: "nickname@domain.com")
850 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
852 assert json_response(conn, 200) == %{
857 "deactivated" => user.deactivated,
859 "nickname" => user.nickname,
860 "roles" => %{"admin" => false, "moderator" => false},
863 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
864 "display_name" => HTML.strip_tags(user.name || user.nickname),
865 "confirmation_pending" => false
871 test "search by display name", %{conn: conn} do
872 user = insert(:user, name: "Display name")
875 conn = get(conn, "/api/pleroma/admin/users?name=display")
877 assert json_response(conn, 200) == %{
882 "deactivated" => user.deactivated,
884 "nickname" => user.nickname,
885 "roles" => %{"admin" => false, "moderator" => false},
888 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
889 "display_name" => HTML.strip_tags(user.name || user.nickname),
890 "confirmation_pending" => false
896 test "search by email", %{conn: conn} do
897 user = insert(:user, email: "email@example.com")
900 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
902 assert json_response(conn, 200) == %{
907 "deactivated" => user.deactivated,
909 "nickname" => user.nickname,
910 "roles" => %{"admin" => false, "moderator" => false},
913 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
914 "display_name" => HTML.strip_tags(user.name || user.nickname),
915 "confirmation_pending" => false
921 test "regular search with page size", %{conn: conn} do
922 user = insert(:user, nickname: "aalice")
923 user2 = insert(:user, nickname: "alice")
925 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
927 assert json_response(conn1, 200) == %{
932 "deactivated" => user.deactivated,
934 "nickname" => user.nickname,
935 "roles" => %{"admin" => false, "moderator" => false},
938 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
939 "display_name" => HTML.strip_tags(user.name || user.nickname),
940 "confirmation_pending" => false
945 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
947 assert json_response(conn2, 200) == %{
952 "deactivated" => user2.deactivated,
954 "nickname" => user2.nickname,
955 "roles" => %{"admin" => false, "moderator" => false},
958 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
959 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
960 "confirmation_pending" => false
966 test "only local users" do
967 admin = insert(:user, is_admin: true, nickname: "john")
968 token = insert(:oauth_admin_token, user: admin)
969 user = insert(:user, nickname: "bob")
971 insert(:user, nickname: "bobb", local: false)
975 |> assign(:user, admin)
976 |> assign(:token, token)
977 |> get("/api/pleroma/admin/users?query=bo&filters=local")
979 assert json_response(conn, 200) == %{
984 "deactivated" => user.deactivated,
986 "nickname" => user.nickname,
987 "roles" => %{"admin" => false, "moderator" => false},
990 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
991 "display_name" => HTML.strip_tags(user.name || user.nickname),
992 "confirmation_pending" => false
998 test "only local users with no query", %{conn: conn, admin: old_admin} do
999 admin = insert(:user, is_admin: true, nickname: "john")
1000 user = insert(:user, nickname: "bob")
1002 insert(:user, nickname: "bobb", local: false)
1004 conn = get(conn, "/api/pleroma/admin/users?filters=local")
1009 "deactivated" => user.deactivated,
1011 "nickname" => user.nickname,
1012 "roles" => %{"admin" => false, "moderator" => false},
1015 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1016 "display_name" => HTML.strip_tags(user.name || user.nickname),
1017 "confirmation_pending" => false
1020 "deactivated" => admin.deactivated,
1022 "nickname" => admin.nickname,
1023 "roles" => %{"admin" => true, "moderator" => false},
1026 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1027 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1028 "confirmation_pending" => false
1031 "deactivated" => false,
1032 "id" => old_admin.id,
1034 "nickname" => old_admin.nickname,
1035 "roles" => %{"admin" => true, "moderator" => false},
1037 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
1038 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
1039 "confirmation_pending" => false
1042 |> Enum.sort_by(& &1["nickname"])
1044 assert json_response(conn, 200) == %{
1051 test "load only admins", %{conn: conn, admin: admin} do
1052 second_admin = insert(:user, is_admin: true)
1056 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
1061 "deactivated" => false,
1063 "nickname" => admin.nickname,
1064 "roles" => %{"admin" => true, "moderator" => false},
1065 "local" => admin.local,
1067 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1068 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1069 "confirmation_pending" => false
1072 "deactivated" => false,
1073 "id" => second_admin.id,
1074 "nickname" => second_admin.nickname,
1075 "roles" => %{"admin" => true, "moderator" => false},
1076 "local" => second_admin.local,
1078 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
1079 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
1080 "confirmation_pending" => false
1083 |> Enum.sort_by(& &1["nickname"])
1085 assert json_response(conn, 200) == %{
1092 test "load only moderators", %{conn: conn} do
1093 moderator = insert(:user, is_moderator: true)
1097 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
1099 assert json_response(conn, 200) == %{
1104 "deactivated" => false,
1105 "id" => moderator.id,
1106 "nickname" => moderator.nickname,
1107 "roles" => %{"admin" => false, "moderator" => true},
1108 "local" => moderator.local,
1110 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
1111 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
1112 "confirmation_pending" => false
1118 test "load users with tags list", %{conn: conn} do
1119 user1 = insert(:user, tags: ["first"])
1120 user2 = insert(:user, tags: ["second"])
1124 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1129 "deactivated" => false,
1131 "nickname" => user1.nickname,
1132 "roles" => %{"admin" => false, "moderator" => false},
1133 "local" => user1.local,
1134 "tags" => ["first"],
1135 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1136 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1137 "confirmation_pending" => false
1140 "deactivated" => false,
1142 "nickname" => user2.nickname,
1143 "roles" => %{"admin" => false, "moderator" => false},
1144 "local" => user2.local,
1145 "tags" => ["second"],
1146 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1147 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1148 "confirmation_pending" => false
1151 |> Enum.sort_by(& &1["nickname"])
1153 assert json_response(conn, 200) == %{
1160 test "it works with multiple filters" do
1161 admin = insert(:user, nickname: "john", is_admin: true)
1162 token = insert(:oauth_admin_token, user: admin)
1163 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1165 insert(:user, nickname: "ken", local: true, deactivated: true)
1166 insert(:user, nickname: "bobb", local: false, deactivated: false)
1170 |> assign(:user, admin)
1171 |> assign(:token, token)
1172 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1174 assert json_response(conn, 200) == %{
1179 "deactivated" => user.deactivated,
1181 "nickname" => user.nickname,
1182 "roles" => %{"admin" => false, "moderator" => false},
1183 "local" => user.local,
1185 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1186 "display_name" => HTML.strip_tags(user.name || user.nickname),
1187 "confirmation_pending" => false
1193 test "it omits relay user", %{admin: admin, conn: conn} do
1194 assert %User{} = Relay.get_actor()
1196 conn = get(conn, "/api/pleroma/admin/users")
1198 assert json_response(conn, 200) == %{
1203 "deactivated" => admin.deactivated,
1205 "nickname" => admin.nickname,
1206 "roles" => %{"admin" => true, "moderator" => false},
1209 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1210 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1211 "confirmation_pending" => false
1218 test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
1219 user_one = insert(:user, deactivated: true)
1220 user_two = insert(:user, deactivated: true)
1225 "/api/pleroma/admin/users/activate",
1226 %{nicknames: [user_one.nickname, user_two.nickname]}
1229 response = json_response(conn, 200)
1230 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1232 log_entry = Repo.one(ModerationLog)
1234 assert ModerationLog.get_log_entry_message(log_entry) ==
1235 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1238 test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
1239 user_one = insert(:user, deactivated: false)
1240 user_two = insert(:user, deactivated: false)
1245 "/api/pleroma/admin/users/deactivate",
1246 %{nicknames: [user_one.nickname, user_two.nickname]}
1249 response = json_response(conn, 200)
1250 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1252 log_entry = Repo.one(ModerationLog)
1254 assert ModerationLog.get_log_entry_message(log_entry) ==
1255 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1258 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
1259 user = insert(:user)
1261 conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1263 assert json_response(conn, 200) ==
1265 "deactivated" => !user.deactivated,
1267 "nickname" => user.nickname,
1268 "roles" => %{"admin" => false, "moderator" => false},
1271 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1272 "display_name" => HTML.strip_tags(user.name || user.nickname),
1273 "confirmation_pending" => false
1276 log_entry = Repo.one(ModerationLog)
1278 assert ModerationLog.get_log_entry_message(log_entry) ==
1279 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1282 describe "PUT disable_mfa" do
1283 test "returns 200 and disable 2fa", %{conn: conn} do
1286 multi_factor_authentication_settings: %MFA.Settings{
1288 totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
1294 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
1295 |> json_response(200)
1297 assert response == user.nickname
1298 mfa_settings = refresh_record(user).multi_factor_authentication_settings
1300 refute mfa_settings.enabled
1301 refute mfa_settings.totp.confirmed
1304 test "returns 404 if user not found", %{conn: conn} do
1307 |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
1308 |> json_response(404)
1310 assert response == "Not found"
1314 describe "POST /api/pleroma/admin/users/invite_token" do
1315 test "without options", %{conn: conn} do
1316 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1318 invite_json = json_response(conn, 200)
1319 invite = UserInviteToken.find_by_token!(invite_json["token"])
1321 refute invite.expires_at
1322 refute invite.max_use
1323 assert invite.invite_type == "one_time"
1326 test "with expires_at", %{conn: conn} do
1328 post(conn, "/api/pleroma/admin/users/invite_token", %{
1329 "expires_at" => Date.to_string(Date.utc_today())
1332 invite_json = json_response(conn, 200)
1333 invite = UserInviteToken.find_by_token!(invite_json["token"])
1336 assert invite.expires_at == Date.utc_today()
1337 refute invite.max_use
1338 assert invite.invite_type == "date_limited"
1341 test "with max_use", %{conn: conn} do
1342 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1344 invite_json = json_response(conn, 200)
1345 invite = UserInviteToken.find_by_token!(invite_json["token"])
1347 refute invite.expires_at
1348 assert invite.max_use == 150
1349 assert invite.invite_type == "reusable"
1352 test "with max use and expires_at", %{conn: conn} do
1354 post(conn, "/api/pleroma/admin/users/invite_token", %{
1356 "expires_at" => Date.to_string(Date.utc_today())
1359 invite_json = json_response(conn, 200)
1360 invite = UserInviteToken.find_by_token!(invite_json["token"])
1362 assert invite.expires_at == Date.utc_today()
1363 assert invite.max_use == 150
1364 assert invite.invite_type == "reusable_date_limited"
1368 describe "GET /api/pleroma/admin/users/invites" do
1369 test "no invites", %{conn: conn} do
1370 conn = get(conn, "/api/pleroma/admin/users/invites")
1372 assert json_response(conn, 200) == %{"invites" => []}
1375 test "with invite", %{conn: conn} do
1376 {:ok, invite} = UserInviteToken.create_invite()
1378 conn = get(conn, "/api/pleroma/admin/users/invites")
1380 assert json_response(conn, 200) == %{
1383 "expires_at" => nil,
1385 "invite_type" => "one_time",
1387 "token" => invite.token,
1396 describe "POST /api/pleroma/admin/users/revoke_invite" do
1397 test "with token", %{conn: conn} do
1398 {:ok, invite} = UserInviteToken.create_invite()
1400 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1402 assert json_response(conn, 200) == %{
1403 "expires_at" => nil,
1405 "invite_type" => "one_time",
1407 "token" => invite.token,
1413 test "with invalid token", %{conn: conn} do
1414 conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1416 assert json_response(conn, :not_found) == "Not found"
1420 describe "GET /api/pleroma/admin/reports/:id" do
1421 test "returns report by its id", %{conn: conn} do
1422 [reporter, target_user] = insert_pair(:user)
1423 activity = insert(:note_activity, user: target_user)
1425 {:ok, %{id: report_id}} =
1426 CommonAPI.report(reporter, %{
1427 account_id: target_user.id,
1428 comment: "I feel offended",
1429 status_ids: [activity.id]
1434 |> get("/api/pleroma/admin/reports/#{report_id}")
1435 |> json_response(:ok)
1437 assert response["id"] == report_id
1440 test "returns 404 when report id is invalid", %{conn: conn} do
1441 conn = get(conn, "/api/pleroma/admin/reports/test")
1443 assert json_response(conn, :not_found) == "Not found"
1447 describe "PATCH /api/pleroma/admin/reports" do
1449 [reporter, target_user] = insert_pair(:user)
1450 activity = insert(:note_activity, user: target_user)
1452 {:ok, %{id: report_id}} =
1453 CommonAPI.report(reporter, %{
1454 account_id: target_user.id,
1455 comment: "I feel offended",
1456 status_ids: [activity.id]
1459 {:ok, %{id: second_report_id}} =
1460 CommonAPI.report(reporter, %{
1461 account_id: target_user.id,
1462 comment: "I feel very offended",
1463 status_ids: [activity.id]
1468 second_report_id: second_report_id
1472 test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
1473 read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
1474 write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
1478 |> assign(:token, read_token)
1479 |> patch("/api/pleroma/admin/reports", %{
1480 "reports" => [%{"state" => "resolved", "id" => id}]
1482 |> json_response(403)
1484 assert response == %{
1485 "error" => "Insufficient permissions: admin:write:reports."
1489 |> assign(:token, write_token)
1490 |> patch("/api/pleroma/admin/reports", %{
1491 "reports" => [%{"state" => "resolved", "id" => id}]
1493 |> json_response(:no_content)
1496 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1498 |> patch("/api/pleroma/admin/reports", %{
1500 %{"state" => "resolved", "id" => id}
1503 |> json_response(:no_content)
1505 activity = Activity.get_by_id(id)
1506 assert activity.data["state"] == "resolved"
1508 log_entry = Repo.one(ModerationLog)
1510 assert ModerationLog.get_log_entry_message(log_entry) ==
1511 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1514 test "closes report", %{conn: conn, id: id, admin: admin} do
1516 |> patch("/api/pleroma/admin/reports", %{
1518 %{"state" => "closed", "id" => id}
1521 |> json_response(:no_content)
1523 activity = Activity.get_by_id(id)
1524 assert activity.data["state"] == "closed"
1526 log_entry = Repo.one(ModerationLog)
1528 assert ModerationLog.get_log_entry_message(log_entry) ==
1529 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1532 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1535 |> patch("/api/pleroma/admin/reports", %{
1537 %{"state" => "test", "id" => id}
1541 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1544 test "returns 404 when report is not exist", %{conn: conn} do
1547 |> patch("/api/pleroma/admin/reports", %{
1549 %{"state" => "closed", "id" => "test"}
1553 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1556 test "updates state of multiple reports", %{
1560 second_report_id: second_report_id
1563 |> patch("/api/pleroma/admin/reports", %{
1565 %{"state" => "resolved", "id" => id},
1566 %{"state" => "closed", "id" => second_report_id}
1569 |> json_response(:no_content)
1571 activity = Activity.get_by_id(id)
1572 second_activity = Activity.get_by_id(second_report_id)
1573 assert activity.data["state"] == "resolved"
1574 assert second_activity.data["state"] == "closed"
1576 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1578 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1579 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1581 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1582 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1586 describe "GET /api/pleroma/admin/reports" do
1587 test "returns empty response when no reports created", %{conn: conn} do
1590 |> get("/api/pleroma/admin/reports")
1591 |> json_response(:ok)
1593 assert Enum.empty?(response["reports"])
1594 assert response["total"] == 0
1597 test "returns reports", %{conn: conn} do
1598 [reporter, target_user] = insert_pair(:user)
1599 activity = insert(:note_activity, user: target_user)
1601 {:ok, %{id: report_id}} =
1602 CommonAPI.report(reporter, %{
1603 account_id: target_user.id,
1604 comment: "I feel offended",
1605 status_ids: [activity.id]
1610 |> get("/api/pleroma/admin/reports")
1611 |> json_response(:ok)
1613 [report] = response["reports"]
1615 assert length(response["reports"]) == 1
1616 assert report["id"] == report_id
1618 assert response["total"] == 1
1621 test "returns reports with specified state", %{conn: conn} do
1622 [reporter, target_user] = insert_pair(:user)
1623 activity = insert(:note_activity, user: target_user)
1625 {:ok, %{id: first_report_id}} =
1626 CommonAPI.report(reporter, %{
1627 account_id: target_user.id,
1628 comment: "I feel offended",
1629 status_ids: [activity.id]
1632 {:ok, %{id: second_report_id}} =
1633 CommonAPI.report(reporter, %{
1634 account_id: target_user.id,
1635 comment: "I don't like this user"
1638 CommonAPI.update_report_state(second_report_id, "closed")
1642 |> get("/api/pleroma/admin/reports", %{
1645 |> json_response(:ok)
1647 [open_report] = response["reports"]
1649 assert length(response["reports"]) == 1
1650 assert open_report["id"] == first_report_id
1652 assert response["total"] == 1
1656 |> get("/api/pleroma/admin/reports", %{
1659 |> json_response(:ok)
1661 [closed_report] = response["reports"]
1663 assert length(response["reports"]) == 1
1664 assert closed_report["id"] == second_report_id
1666 assert response["total"] == 1
1670 |> get("/api/pleroma/admin/reports", %{
1671 "state" => "resolved"
1673 |> json_response(:ok)
1675 assert Enum.empty?(response["reports"])
1676 assert response["total"] == 0
1679 test "returns 403 when requested by a non-admin" do
1680 user = insert(:user)
1681 token = insert(:oauth_token, user: user)
1685 |> assign(:user, user)
1686 |> assign(:token, token)
1687 |> get("/api/pleroma/admin/reports")
1689 assert json_response(conn, :forbidden) ==
1690 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1693 test "returns 403 when requested by anonymous" do
1694 conn = get(build_conn(), "/api/pleroma/admin/reports")
1696 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1700 describe "GET /api/pleroma/admin/statuses/:id" do
1701 test "not found", %{conn: conn} do
1703 |> get("/api/pleroma/admin/statuses/not_found")
1704 |> json_response(:not_found)
1707 test "shows activity", %{conn: conn} do
1708 activity = insert(:note_activity)
1712 |> get("/api/pleroma/admin/statuses/#{activity.id}")
1713 |> json_response(200)
1715 assert response["id"] == activity.id
1719 describe "PUT /api/pleroma/admin/statuses/:id" do
1721 activity = insert(:note_activity)
1726 test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
1729 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
1730 |> json_response(:ok)
1732 assert response["sensitive"]
1734 log_entry = Repo.one(ModerationLog)
1736 assert ModerationLog.get_log_entry_message(log_entry) ==
1737 "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
1741 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
1742 |> json_response(:ok)
1744 refute response["sensitive"]
1747 test "change visibility flag", %{conn: conn, id: id, admin: admin} do
1750 |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"})
1751 |> json_response(:ok)
1753 assert response["visibility"] == "public"
1755 log_entry = Repo.one(ModerationLog)
1757 assert ModerationLog.get_log_entry_message(log_entry) ==
1758 "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
1762 |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"})
1763 |> json_response(:ok)
1765 assert response["visibility"] == "private"
1769 |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"})
1770 |> json_response(:ok)
1772 assert response["visibility"] == "unlisted"
1775 test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
1776 conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{visibility: "test"})
1778 assert json_response(conn, :bad_request) == "Unsupported visibility"
1782 describe "DELETE /api/pleroma/admin/statuses/:id" do
1784 activity = insert(:note_activity)
1789 test "deletes status", %{conn: conn, id: id, admin: admin} do
1791 |> delete("/api/pleroma/admin/statuses/#{id}")
1792 |> json_response(:ok)
1794 refute Activity.get_by_id(id)
1796 log_entry = Repo.one(ModerationLog)
1798 assert ModerationLog.get_log_entry_message(log_entry) ==
1799 "@#{admin.nickname} deleted status ##{id}"
1802 test "returns 404 when the status does not exist", %{conn: conn} do
1803 conn = delete(conn, "/api/pleroma/admin/statuses/test")
1805 assert json_response(conn, :not_found) == "Not found"
1809 describe "GET /api/pleroma/admin/config" do
1810 setup do: clear_config(:configurable_from_database, true)
1812 test "when configuration from database is off", %{conn: conn} do
1813 Config.put(:configurable_from_database, false)
1814 conn = get(conn, "/api/pleroma/admin/config")
1816 assert json_response(conn, 400) ==
1817 "To use this endpoint you need to enable configuration from database."
1820 test "with settings only in db", %{conn: conn} do
1821 config1 = insert(:config)
1822 config2 = insert(:config)
1824 conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
1829 "group" => ":pleroma",
1834 "group" => ":pleroma",
1839 } = json_response(conn, 200)
1841 assert key1 == config1.key
1842 assert key2 == config2.key
1845 test "db is added to settings that are in db", %{conn: conn} do
1846 _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
1848 %{"configs" => configs} =
1850 |> get("/api/pleroma/admin/config")
1851 |> json_response(200)
1854 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1855 group == ":pleroma" and key == ":instance"
1858 assert instance_config["db"] == [":name"]
1861 test "merged default setting with db settings", %{conn: conn} do
1862 config1 = insert(:config)
1863 config2 = insert(:config)
1867 value: ConfigDB.to_binary(k1: :v1, k2: :v2)
1870 %{"configs" => configs} =
1872 |> get("/api/pleroma/admin/config")
1873 |> json_response(200)
1875 assert length(configs) > 3
1878 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1879 group == ":pleroma" and key in [config1.key, config2.key, config3.key]
1882 assert length(received_configs) == 3
1886 |> ConfigDB.from_binary()
1888 |> ConfigDB.convert()
1890 Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
1891 assert db in [[config1.key], [config2.key], db_keys]
1894 ConfigDB.from_binary_with_convert(config1.value),
1895 ConfigDB.from_binary_with_convert(config2.value),
1896 ConfigDB.from_binary_with_convert(config3.value)
1901 test "subkeys with full update right merge", %{conn: conn} do
1905 value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
1911 value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
1914 %{"configs" => configs} =
1916 |> get("/api/pleroma/admin/config")
1917 |> json_response(200)
1920 Enum.filter(configs, fn %{"group" => group, "key" => key} ->
1921 group == ":pleroma" and key in [config1.key, config2.key]
1924 emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
1925 assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
1927 emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
1928 assets_val = ConfigDB.transform_with_out_binary(assets["value"])
1930 assert emoji_val[:groups] == [a: 1, b: 2]
1931 assert assets_val[:mascots] == [a: 1, b: 2]
1935 test "POST /api/pleroma/admin/config error", %{conn: conn} do
1936 conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
1938 assert json_response(conn, 400) ==
1939 "To use this endpoint you need to enable configuration from database."
1942 describe "POST /api/pleroma/admin/config" do
1944 http = Application.get_env(:pleroma, :http)
1947 Application.delete_env(:pleroma, :key1)
1948 Application.delete_env(:pleroma, :key2)
1949 Application.delete_env(:pleroma, :key3)
1950 Application.delete_env(:pleroma, :key4)
1951 Application.delete_env(:pleroma, :keyaa1)
1952 Application.delete_env(:pleroma, :keyaa2)
1953 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1954 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1955 Application.put_env(:pleroma, :http, http)
1956 Application.put_env(:tesla, :adapter, Tesla.Mock)
1957 Restarter.Pleroma.refresh()
1961 setup do: clear_config(:configurable_from_database, true)
1963 @tag capture_log: true
1964 test "create new config setting in db", %{conn: conn} do
1965 ueberauth = Application.get_env(:ueberauth, Ueberauth)
1966 on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
1969 post(conn, "/api/pleroma/admin/config", %{
1971 %{group: ":pleroma", key: ":key1", value: "value1"},
1973 group: ":ueberauth",
1975 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1981 ":nested_1" => "nested_value1",
1983 %{":nested_22" => "nested_value222"},
1984 %{":nested_33" => %{":nested_44" => "nested_444"}}
1992 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1993 %{"nested_4" => true}
1999 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
2004 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
2009 assert json_response(conn, 200) == %{
2012 "group" => ":pleroma",
2014 "value" => "value1",
2018 "group" => ":ueberauth",
2019 "key" => "Ueberauth",
2020 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
2021 "db" => [":consumer_secret"]
2024 "group" => ":pleroma",
2027 ":nested_1" => "nested_value1",
2029 %{":nested_22" => "nested_value222"},
2030 %{":nested_33" => %{":nested_44" => "nested_444"}}
2036 "group" => ":pleroma",
2039 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
2040 %{"nested_4" => true}
2045 "group" => ":pleroma",
2047 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
2053 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
2059 assert Application.get_env(:pleroma, :key1) == "value1"
2061 assert Application.get_env(:pleroma, :key2) == %{
2062 nested_1: "nested_value1",
2064 %{nested_22: "nested_value222"},
2065 %{nested_33: %{nested_44: "nested_444"}}
2069 assert Application.get_env(:pleroma, :key3) == [
2070 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
2071 %{"nested_4" => true}
2074 assert Application.get_env(:pleroma, :key4) == %{
2075 "endpoint" => "https://example.com",
2079 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
2082 test "save configs setting without explicit key", %{conn: conn} do
2083 level = Application.get_env(:quack, :level)
2084 meta = Application.get_env(:quack, :meta)
2085 webhook_url = Application.get_env(:quack, :webhook_url)
2088 Application.put_env(:quack, :level, level)
2089 Application.put_env(:quack, :meta, meta)
2090 Application.put_env(:quack, :webhook_url, webhook_url)
2094 post(conn, "/api/pleroma/admin/config", %{
2108 key: ":webhook_url",
2109 value: "https://hooks.slack.com/services/KEY"
2114 assert json_response(conn, 200) == %{
2117 "group" => ":quack",
2123 "group" => ":quack",
2125 "value" => [":none"],
2129 "group" => ":quack",
2130 "key" => ":webhook_url",
2131 "value" => "https://hooks.slack.com/services/KEY",
2132 "db" => [":webhook_url"]
2137 assert Application.get_env(:quack, :level) == :info
2138 assert Application.get_env(:quack, :meta) == [:none]
2139 assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
2142 test "saving config with partial update", %{conn: conn} do
2143 config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
2146 post(conn, "/api/pleroma/admin/config", %{
2148 %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
2152 assert json_response(conn, 200) == %{
2155 "group" => ":pleroma",
2158 %{"tuple" => [":key1", 1]},
2159 %{"tuple" => [":key2", 2]},
2160 %{"tuple" => [":key3", 3]}
2162 "db" => [":key1", ":key2", ":key3"]
2168 test "saving config which need pleroma reboot", %{conn: conn} do
2169 chat = Config.get(:chat)
2170 on_exit(fn -> Config.put(:chat, chat) end)
2174 "/api/pleroma/admin/config",
2177 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2181 |> json_response(200) == %{
2184 "db" => [":enabled"],
2185 "group" => ":pleroma",
2187 "value" => [%{"tuple" => [":enabled", true]}]
2190 "need_reboot" => true
2195 |> get("/api/pleroma/admin/config")
2196 |> json_response(200)
2198 assert configs["need_reboot"]
2201 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2202 end) =~ "pleroma restarted"
2206 |> get("/api/pleroma/admin/config")
2207 |> json_response(200)
2209 assert configs["need_reboot"] == false
2212 test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
2213 chat = Config.get(:chat)
2214 on_exit(fn -> Config.put(:chat, chat) end)
2218 "/api/pleroma/admin/config",
2221 %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
2225 |> json_response(200) == %{
2228 "db" => [":enabled"],
2229 "group" => ":pleroma",
2231 "value" => [%{"tuple" => [":enabled", true]}]
2234 "need_reboot" => true
2237 assert post(conn, "/api/pleroma/admin/config", %{
2239 %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
2242 |> json_response(200) == %{
2245 "group" => ":pleroma",
2248 %{"tuple" => [":key3", 3]}
2253 "need_reboot" => true
2257 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2258 end) =~ "pleroma restarted"
2262 |> get("/api/pleroma/admin/config")
2263 |> json_response(200)
2265 assert configs["need_reboot"] == false
2268 test "saving config with nested merge", %{conn: conn} do
2270 insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
2273 post(conn, "/api/pleroma/admin/config", %{
2276 group: config.group,
2279 %{"tuple" => [":key3", 3]},
2284 %{"tuple" => [":k2", 1]},
2285 %{"tuple" => [":k3", 3]}
2294 assert json_response(conn, 200) == %{
2297 "group" => ":pleroma",
2300 %{"tuple" => [":key1", 1]},
2301 %{"tuple" => [":key3", 3]},
2306 %{"tuple" => [":k1", 1]},
2307 %{"tuple" => [":k2", 1]},
2308 %{"tuple" => [":k3", 3]}
2313 "db" => [":key1", ":key3", ":key2"]
2319 test "saving special atoms", %{conn: conn} do
2321 post(conn, "/api/pleroma/admin/config", %{
2324 "group" => ":pleroma",
2330 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2338 assert json_response(conn, 200) == %{
2341 "group" => ":pleroma",
2347 [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
2351 "db" => [":ssl_options"]
2356 assert Application.get_env(:pleroma, :key1) == [
2357 ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
2361 test "saving full setting if value is in full_key_update list", %{conn: conn} do
2362 backends = Application.get_env(:logger, :backends)
2363 on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
2369 value: :erlang.term_to_binary([])
2372 Pleroma.Config.TransferTask.load_and_update_env([], false)
2374 assert Application.get_env(:logger, :backends) == []
2377 post(conn, "/api/pleroma/admin/config", %{
2380 group: config.group,
2387 assert json_response(conn, 200) == %{
2390 "group" => ":logger",
2391 "key" => ":backends",
2395 "db" => [":backends"]
2400 assert Application.get_env(:logger, :backends) == [
2405 test "saving full setting if value is not keyword", %{conn: conn} do
2410 value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
2414 post(conn, "/api/pleroma/admin/config", %{
2416 %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
2420 assert json_response(conn, 200) == %{
2423 "group" => ":tesla",
2424 "key" => ":adapter",
2425 "value" => "Tesla.Adapter.Httpc",
2426 "db" => [":adapter"]
2432 test "update config setting & delete with fallback to default value", %{
2437 ueberauth = Application.get_env(:ueberauth, Ueberauth)
2438 config1 = insert(:config, key: ":keyaa1")
2439 config2 = insert(:config, key: ":keyaa2")
2443 group: ":ueberauth",
2448 post(conn, "/api/pleroma/admin/config", %{
2450 %{group: config1.group, key: config1.key, value: "another_value"},
2451 %{group: config2.group, key: config2.key, value: "another_value"}
2455 assert json_response(conn, 200) == %{
2458 "group" => ":pleroma",
2459 "key" => config1.key,
2460 "value" => "another_value",
2464 "group" => ":pleroma",
2465 "key" => config2.key,
2466 "value" => "another_value",
2472 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2473 assert Application.get_env(:pleroma, :keyaa2) == "another_value"
2474 assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
2478 |> assign(:user, admin)
2479 |> assign(:token, token)
2480 |> post("/api/pleroma/admin/config", %{
2482 %{group: config2.group, key: config2.key, delete: true},
2484 group: ":ueberauth",
2491 assert json_response(conn, 200) == %{
2495 assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
2496 refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
2499 test "common config example", %{conn: conn} do
2501 post(conn, "/api/pleroma/admin/config", %{
2504 "group" => ":pleroma",
2505 "key" => "Pleroma.Captcha.NotReal",
2507 %{"tuple" => [":enabled", false]},
2508 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2509 %{"tuple" => [":seconds_valid", 60]},
2510 %{"tuple" => [":path", ""]},
2511 %{"tuple" => [":key1", nil]},
2512 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2513 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2514 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2515 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2516 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
2517 %{"tuple" => [":name", "Pleroma"]}
2523 assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
2525 assert json_response(conn, 200) == %{
2528 "group" => ":pleroma",
2529 "key" => "Pleroma.Captcha.NotReal",
2531 %{"tuple" => [":enabled", false]},
2532 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2533 %{"tuple" => [":seconds_valid", 60]},
2534 %{"tuple" => [":path", ""]},
2535 %{"tuple" => [":key1", nil]},
2536 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2537 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2538 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2539 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2540 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
2541 %{"tuple" => [":name", "Pleroma"]}
2561 test "tuples with more than two values", %{conn: conn} do
2563 post(conn, "/api/pleroma/admin/config", %{
2566 "group" => ":pleroma",
2567 "key" => "Pleroma.Web.Endpoint.NotReal",
2583 "/api/v1/streaming",
2584 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2591 "Phoenix.Endpoint.CowboyWebSocket",
2594 "Phoenix.Transports.WebSocket",
2597 "Pleroma.Web.Endpoint",
2598 "Pleroma.Web.UserSocket",
2609 "Phoenix.Endpoint.Cowboy2Handler",
2610 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2627 assert json_response(conn, 200) == %{
2630 "group" => ":pleroma",
2631 "key" => "Pleroma.Web.Endpoint.NotReal",
2647 "/api/v1/streaming",
2648 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2655 "Phoenix.Endpoint.CowboyWebSocket",
2658 "Phoenix.Transports.WebSocket",
2661 "Pleroma.Web.Endpoint",
2662 "Pleroma.Web.UserSocket",
2673 "Phoenix.Endpoint.Cowboy2Handler",
2674 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2693 test "settings with nesting map", %{conn: conn} do
2695 post(conn, "/api/pleroma/admin/config", %{
2698 "group" => ":pleroma",
2701 %{"tuple" => [":key2", "some_val"]},
2706 ":max_options" => 20,
2707 ":max_option_chars" => 200,
2708 ":min_expiration" => 0,
2709 ":max_expiration" => 31_536_000,
2711 ":max_options" => 20,
2712 ":max_option_chars" => 200,
2713 ":min_expiration" => 0,
2714 ":max_expiration" => 31_536_000
2724 assert json_response(conn, 200) ==
2728 "group" => ":pleroma",
2731 %{"tuple" => [":key2", "some_val"]},
2736 ":max_expiration" => 31_536_000,
2737 ":max_option_chars" => 200,
2738 ":max_options" => 20,
2739 ":min_expiration" => 0,
2741 ":max_expiration" => 31_536_000,
2742 ":max_option_chars" => 200,
2743 ":max_options" => 20,
2744 ":min_expiration" => 0
2750 "db" => [":key2", ":key3"]
2756 test "value as map", %{conn: conn} do
2758 post(conn, "/api/pleroma/admin/config", %{
2761 "group" => ":pleroma",
2763 "value" => %{"key" => "some_val"}
2768 assert json_response(conn, 200) ==
2772 "group" => ":pleroma",
2774 "value" => %{"key" => "some_val"},
2781 test "queues key as atom", %{conn: conn} do
2783 post(conn, "/api/pleroma/admin/config", %{
2789 %{"tuple" => [":federator_incoming", 50]},
2790 %{"tuple" => [":federator_outgoing", 50]},
2791 %{"tuple" => [":web_push", 50]},
2792 %{"tuple" => [":mailer", 10]},
2793 %{"tuple" => [":transmogrifier", 20]},
2794 %{"tuple" => [":scheduled_activities", 10]},
2795 %{"tuple" => [":background", 5]}
2801 assert json_response(conn, 200) == %{
2807 %{"tuple" => [":federator_incoming", 50]},
2808 %{"tuple" => [":federator_outgoing", 50]},
2809 %{"tuple" => [":web_push", 50]},
2810 %{"tuple" => [":mailer", 10]},
2811 %{"tuple" => [":transmogrifier", 20]},
2812 %{"tuple" => [":scheduled_activities", 10]},
2813 %{"tuple" => [":background", 5]}
2816 ":federator_incoming",
2817 ":federator_outgoing",
2821 ":scheduled_activities",
2829 test "delete part of settings by atom subkeys", %{conn: conn} do
2833 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2837 post(conn, "/api/pleroma/admin/config", %{
2840 group: config.group,
2842 subkeys: [":subkey1", ":subkey3"],
2848 assert json_response(conn, 200) == %{
2851 "group" => ":pleroma",
2853 "value" => [%{"tuple" => [":subkey2", "val2"]}],
2854 "db" => [":subkey2"]
2860 test "proxy tuple localhost", %{conn: conn} do
2862 post(conn, "/api/pleroma/admin/config", %{
2868 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
2877 "group" => ":pleroma",
2883 } = json_response(conn, 200)
2885 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
2886 assert ":proxy_url" in db
2889 test "proxy tuple domain", %{conn: conn} do
2891 post(conn, "/api/pleroma/admin/config", %{
2897 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
2906 "group" => ":pleroma",
2912 } = json_response(conn, 200)
2914 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
2915 assert ":proxy_url" in db
2918 test "proxy tuple ip", %{conn: conn} do
2920 post(conn, "/api/pleroma/admin/config", %{
2926 %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
2935 "group" => ":pleroma",
2941 } = json_response(conn, 200)
2943 assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
2944 assert ":proxy_url" in db
2947 test "doesn't set keys not in the whitelist", %{conn: conn} do
2948 clear_config(:database_config_whitelist, [
2951 {:pleroma, Pleroma.Captcha.NotReal},
2955 post(conn, "/api/pleroma/admin/config", %{
2957 %{group: ":pleroma", key: ":key1", value: "value1"},
2958 %{group: ":pleroma", key: ":key2", value: "value2"},
2959 %{group: ":pleroma", key: ":key3", value: "value3"},
2960 %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
2961 %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
2962 %{group: ":not_real", key: ":anything", value: "value6"}
2966 assert Application.get_env(:pleroma, :key1) == "value1"
2967 assert Application.get_env(:pleroma, :key2) == "value2"
2968 assert Application.get_env(:pleroma, :key3) == nil
2969 assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
2970 assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
2971 assert Application.get_env(:not_real, :anything) == "value6"
2975 describe "GET /api/pleroma/admin/restart" do
2976 setup do: clear_config(:configurable_from_database, true)
2978 test "pleroma restarts", %{conn: conn} do
2980 assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
2981 end) =~ "pleroma restarted"
2983 refute Restarter.Pleroma.need_reboot?()
2987 test "need_reboot flag", %{conn: conn} do
2989 |> get("/api/pleroma/admin/need_reboot")
2990 |> json_response(200) == %{"need_reboot" => false}
2992 Restarter.Pleroma.need_reboot()
2995 |> get("/api/pleroma/admin/need_reboot")
2996 |> json_response(200) == %{"need_reboot" => true}
2998 on_exit(fn -> Restarter.Pleroma.refresh() end)
3001 describe "GET /api/pleroma/admin/statuses" do
3002 test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
3003 blocked = insert(:user)
3004 user = insert(:user)
3005 User.block(admin, blocked)
3007 {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})
3009 {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
3010 {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
3011 {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
3012 {:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"})
3016 |> get("/api/pleroma/admin/statuses")
3017 |> json_response(200)
3019 refute "private" in Enum.map(response, & &1["visibility"])
3020 assert length(response) == 3
3023 test "returns only local statuses with local_only on", %{conn: conn} do
3024 user = insert(:user)
3025 remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
3026 insert(:note_activity, user: user, local: true)
3027 insert(:note_activity, user: remote_user, local: false)
3031 |> get("/api/pleroma/admin/statuses?local_only=true")
3032 |> json_response(200)
3034 assert length(response) == 1
3037 test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
3038 user = insert(:user)
3040 {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"})
3042 {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"})
3043 {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"})
3044 conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
3045 assert json_response(conn, 200) |> length() == 3
3049 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
3051 user = insert(:user)
3053 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
3054 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
3055 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
3057 insert(:note_activity, user: user, published: date1)
3058 insert(:note_activity, user: user, published: date2)
3059 insert(:note_activity, user: user, published: date3)
3064 test "renders user's statuses", %{conn: conn, user: user} do
3065 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
3067 assert json_response(conn, 200) |> length() == 3
3070 test "renders user's statuses with a limit", %{conn: conn, user: user} do
3071 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
3073 assert json_response(conn, 200) |> length() == 2
3076 test "doesn't return private statuses by default", %{conn: conn, user: user} do
3077 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
3079 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
3081 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
3083 assert json_response(conn, 200) |> length() == 4
3086 test "returns private statuses with godmode on", %{conn: conn, user: user} do
3087 {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
3089 {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
3091 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
3093 assert json_response(conn, 200) |> length() == 5
3096 test "excludes reblogs by default", %{conn: conn, user: user} do
3097 other_user = insert(:user)
3098 {:ok, activity} = CommonAPI.post(user, %{status: "."})
3099 {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
3101 conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
3102 assert json_response(conn_res, 200) |> length() == 0
3105 get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
3107 assert json_response(conn_res, 200) |> length() == 1
3111 describe "GET /api/pleroma/admin/moderation_log" do
3113 moderator = insert(:user, is_moderator: true)
3115 %{moderator: moderator}
3118 test "returns the log", %{conn: conn, admin: admin} do
3119 Repo.insert(%ModerationLog{
3123 "nickname" => admin.nickname,
3126 action: "relay_follow",
3127 target: "https://example.org/relay"
3129 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3132 Repo.insert(%ModerationLog{
3136 "nickname" => admin.nickname,
3139 action: "relay_unfollow",
3140 target: "https://example.org/relay"
3142 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3145 conn = get(conn, "/api/pleroma/admin/moderation_log")
3147 response = json_response(conn, 200)
3148 [first_entry, second_entry] = response["items"]
3150 assert response["total"] == 2
3151 assert first_entry["data"]["action"] == "relay_unfollow"
3153 assert first_entry["message"] ==
3154 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3156 assert second_entry["data"]["action"] == "relay_follow"
3158 assert second_entry["message"] ==
3159 "@#{admin.nickname} followed relay: https://example.org/relay"
3162 test "returns the log with pagination", %{conn: conn, admin: admin} do
3163 Repo.insert(%ModerationLog{
3167 "nickname" => admin.nickname,
3170 action: "relay_follow",
3171 target: "https://example.org/relay"
3173 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
3176 Repo.insert(%ModerationLog{
3180 "nickname" => admin.nickname,
3183 action: "relay_unfollow",
3184 target: "https://example.org/relay"
3186 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
3189 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
3191 response1 = json_response(conn1, 200)
3192 [first_entry] = response1["items"]
3194 assert response1["total"] == 2
3195 assert response1["items"] |> length() == 1
3196 assert first_entry["data"]["action"] == "relay_unfollow"
3198 assert first_entry["message"] ==
3199 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3201 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
3203 response2 = json_response(conn2, 200)
3204 [second_entry] = response2["items"]
3206 assert response2["total"] == 2
3207 assert response2["items"] |> length() == 1
3208 assert second_entry["data"]["action"] == "relay_follow"
3210 assert second_entry["message"] ==
3211 "@#{admin.nickname} followed relay: https://example.org/relay"
3214 test "filters log by date", %{conn: conn, admin: admin} do
3215 first_date = "2017-08-15T15:47:06Z"
3216 second_date = "2017-08-20T15:47:06Z"
3218 Repo.insert(%ModerationLog{
3222 "nickname" => admin.nickname,
3225 action: "relay_follow",
3226 target: "https://example.org/relay"
3228 inserted_at: NaiveDateTime.from_iso8601!(first_date)
3231 Repo.insert(%ModerationLog{
3235 "nickname" => admin.nickname,
3238 action: "relay_unfollow",
3239 target: "https://example.org/relay"
3241 inserted_at: NaiveDateTime.from_iso8601!(second_date)
3247 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
3250 response1 = json_response(conn1, 200)
3251 [first_entry] = response1["items"]
3253 assert response1["total"] == 1
3254 assert first_entry["data"]["action"] == "relay_unfollow"
3256 assert first_entry["message"] ==
3257 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
3260 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
3261 Repo.insert(%ModerationLog{
3265 "nickname" => admin.nickname,
3268 action: "relay_follow",
3269 target: "https://example.org/relay"
3273 Repo.insert(%ModerationLog{
3276 "id" => moderator.id,
3277 "nickname" => moderator.nickname,
3280 action: "relay_unfollow",
3281 target: "https://example.org/relay"
3285 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
3287 response1 = json_response(conn1, 200)
3288 [first_entry] = response1["items"]
3290 assert response1["total"] == 1
3291 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
3294 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
3295 ModerationLog.insert_log(%{
3297 action: "relay_follow",
3298 target: "https://example.org/relay"
3301 ModerationLog.insert_log(%{
3303 action: "relay_unfollow",
3304 target: "https://example.org/relay"
3307 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
3309 response1 = json_response(conn1, 200)
3310 [first_entry] = response1["items"]
3312 assert response1["total"] == 1
3314 assert get_in(first_entry, ["data", "message"]) ==
3315 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
3319 describe "GET /users/:nickname/credentials" do
3320 test "gets the user credentials", %{conn: conn} do
3321 user = insert(:user)
3322 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
3324 response = assert json_response(conn, 200)
3325 assert response["email"] == user.email
3328 test "returns 403 if requested by a non-admin" do
3329 user = insert(:user)
3333 |> assign(:user, user)
3334 |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
3336 assert json_response(conn, :forbidden)
3340 describe "PATCH /users/:nickname/credentials" do
3341 test "changes password and email", %{conn: conn, admin: admin} do
3342 user = insert(:user)
3343 assert user.password_reset_pending == false
3346 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3347 "password" => "new_password",
3348 "email" => "new_email@example.com",
3349 "name" => "new_name"
3352 assert json_response(conn, 200) == %{"status" => "success"}
3354 ObanHelpers.perform_all()
3356 updated_user = User.get_by_id(user.id)
3358 assert updated_user.email == "new_email@example.com"
3359 assert updated_user.name == "new_name"
3360 assert updated_user.password_hash != user.password_hash
3361 assert updated_user.password_reset_pending == true
3363 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3365 assert ModerationLog.get_log_entry_message(log_entry1) ==
3366 "@#{admin.nickname} updated users: @#{user.nickname}"
3368 assert ModerationLog.get_log_entry_message(log_entry2) ==
3369 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3372 test "returns 403 if requested by a non-admin" do
3373 user = insert(:user)
3377 |> assign(:user, user)
3378 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3379 "password" => "new_password",
3380 "email" => "new_email@example.com",
3381 "name" => "new_name"
3384 assert json_response(conn, :forbidden)
3388 describe "PATCH /users/:nickname/force_password_reset" do
3389 test "sets password_reset_pending to true", %{conn: conn} do
3390 user = insert(:user)
3391 assert user.password_reset_pending == false
3394 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3396 assert json_response(conn, 204) == ""
3398 ObanHelpers.perform_all()
3400 assert User.get_by_id(user.id).password_reset_pending == true
3404 describe "relays" do
3405 test "POST /relay", %{conn: conn, admin: admin} do
3407 post(conn, "/api/pleroma/admin/relay", %{
3408 relay_url: "http://mastodon.example.org/users/admin"
3411 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3413 log_entry = Repo.one(ModerationLog)
3415 assert ModerationLog.get_log_entry_message(log_entry) ==
3416 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3419 test "GET /relay", %{conn: conn} do
3420 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3422 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3423 |> Enum.each(fn ap_id ->
3424 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3425 User.follow(relay_user, user)
3428 conn = get(conn, "/api/pleroma/admin/relay")
3430 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3433 test "DELETE /relay", %{conn: conn, admin: admin} do
3434 post(conn, "/api/pleroma/admin/relay", %{
3435 relay_url: "http://mastodon.example.org/users/admin"
3439 delete(conn, "/api/pleroma/admin/relay", %{
3440 relay_url: "http://mastodon.example.org/users/admin"
3443 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3445 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3447 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3448 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3450 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3451 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3455 describe "instances" do
3456 test "GET /instances/:instance/statuses", %{conn: conn} do
3457 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3458 user2 = insert(:user, local: false, nickname: "test@test.com")
3459 insert_pair(:note_activity, user: user)
3460 activity = insert(:note_activity, user: user2)
3462 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3464 response = json_response(ret_conn, 200)
3466 assert length(response) == 2
3468 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3470 response = json_response(ret_conn, 200)
3472 assert length(response) == 1
3474 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3476 response = json_response(ret_conn, 200)
3478 assert Enum.empty?(response)
3480 CommonAPI.repeat(activity.id, user)
3482 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3483 response = json_response(ret_conn, 200)
3484 assert length(response) == 2
3486 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3487 response = json_response(ret_conn, 200)
3488 assert length(response) == 3
3492 describe "PATCH /confirm_email" do
3493 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3494 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3496 assert first_user.confirmation_pending == true
3497 assert second_user.confirmation_pending == true
3500 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3502 first_user.nickname,
3503 second_user.nickname
3507 assert ret_conn.status == 200
3509 assert first_user.confirmation_pending == true
3510 assert second_user.confirmation_pending == true
3512 log_entry = Repo.one(ModerationLog)
3514 assert ModerationLog.get_log_entry_message(log_entry) ==
3515 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3516 second_user.nickname
3521 describe "PATCH /resend_confirmation_email" do
3522 test "it resend emails for two users", %{conn: conn, admin: admin} do
3523 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3526 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3528 first_user.nickname,
3529 second_user.nickname
3533 assert ret_conn.status == 200
3535 log_entry = Repo.one(ModerationLog)
3537 assert ModerationLog.get_log_entry_message(log_entry) ==
3538 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3539 second_user.nickname
3544 describe "POST /reports/:id/notes" do
3545 setup %{conn: conn, admin: admin} do
3546 [reporter, target_user] = insert_pair(:user)
3547 activity = insert(:note_activity, user: target_user)
3549 {:ok, %{id: report_id}} =
3550 CommonAPI.report(reporter, %{
3551 account_id: target_user.id,
3552 comment: "I feel offended",
3553 status_ids: [activity.id]
3556 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3557 content: "this is disgusting!"
3560 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3561 content: "this is disgusting2!"
3566 report_id: report_id
3570 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3571 [note, _] = Repo.all(ReportNote)
3574 activity_id: ^report_id,
3575 content: "this is disgusting!",
3580 test "it returns reports with notes", %{conn: conn, admin: admin} do
3581 conn = get(conn, "/api/pleroma/admin/reports")
3583 response = json_response(conn, 200)
3584 notes = hd(response["reports"])["notes"]
3587 assert note["user"]["nickname"] == admin.nickname
3588 assert note["content"] == "this is disgusting!"
3589 assert note["created_at"]
3590 assert response["total"] == 1
3593 test "it deletes the note", %{conn: conn, report_id: report_id} do
3594 assert ReportNote |> Repo.all() |> length() == 2
3596 [note, _] = Repo.all(ReportNote)
3598 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3600 assert ReportNote |> Repo.all() |> length() == 1
3604 describe "GET /api/pleroma/admin/config/descriptions" do
3605 test "structure", %{conn: conn} do
3606 admin = insert(:user, is_admin: true)
3609 assign(conn, :user, admin)
3610 |> get("/api/pleroma/admin/config/descriptions")
3612 assert [child | _others] = json_response(conn, 200)
3614 assert child["children"]
3616 assert String.starts_with?(child["group"], ":")
3617 assert child["description"]
3620 test "filters by database configuration whitelist", %{conn: conn} do
3621 clear_config(:database_config_whitelist, [
3622 {:pleroma, :instance},
3623 {:pleroma, :activitypub},
3624 {:pleroma, Pleroma.Upload},
3628 admin = insert(:user, is_admin: true)
3631 assign(conn, :user, admin)
3632 |> get("/api/pleroma/admin/config/descriptions")
3634 children = json_response(conn, 200)
3636 assert length(children) == 4
3638 assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
3640 instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
3641 assert instance["children"]
3643 activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
3644 assert activitypub["children"]
3646 web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
3647 assert web_endpoint["children"]
3649 esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
3650 assert esshd["children"]
3654 describe "/api/pleroma/admin/stats" do
3655 test "status visibility count", %{conn: conn} do
3656 admin = insert(:user, is_admin: true)
3657 user = insert(:user)
3658 CommonAPI.post(user, %{visibility: "public", status: "hey"})
3659 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3660 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3664 |> assign(:user, admin)
3665 |> get("/api/pleroma/admin/stats")
3666 |> json_response(200)
3668 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3669 response["status_visibility"]
3673 describe "POST /api/pleroma/admin/oauth_app" do
3674 test "errors", %{conn: conn} do
3675 response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
3677 assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
3680 test "success", %{conn: conn} do
3681 base_url = Web.base_url()
3682 app_name = "Trusted app"
3686 |> post("/api/pleroma/admin/oauth_app", %{
3688 redirect_uris: base_url
3690 |> json_response(200)
3694 "client_secret" => _,
3695 "name" => ^app_name,
3696 "redirect_uri" => ^base_url,
3701 test "with trusted", %{conn: conn} do
3702 base_url = Web.base_url()
3703 app_name = "Trusted app"
3707 |> post("/api/pleroma/admin/oauth_app", %{
3709 redirect_uris: base_url,
3712 |> json_response(200)
3716 "client_secret" => _,
3717 "name" => ^app_name,
3718 "redirect_uri" => ^base_url,
3724 describe "GET /api/pleroma/admin/oauth_app" do
3726 app = insert(:oauth_app)
3730 test "list", %{conn: conn} do
3733 |> get("/api/pleroma/admin/oauth_app")
3734 |> json_response(200)
3736 assert %{"apps" => apps, "count" => count, "page_size" => _} = response
3738 assert length(apps) == count
3741 test "with page size", %{conn: conn} do
3747 |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
3748 |> json_response(200)
3750 assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
3752 assert length(apps) == page_size
3755 test "search by client name", %{conn: conn, app: app} do
3758 |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
3759 |> json_response(200)
3761 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3763 assert returned["client_id"] == app.client_id
3764 assert returned["name"] == app.client_name
3767 test "search by client id", %{conn: conn, app: app} do
3770 |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
3771 |> json_response(200)
3773 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3775 assert returned["client_id"] == app.client_id
3776 assert returned["name"] == app.client_name
3779 test "only trusted", %{conn: conn} do
3780 app = insert(:oauth_app, trusted: true)
3784 |> get("/api/pleroma/admin/oauth_app", %{trusted: true})
3785 |> json_response(200)
3787 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3789 assert returned["client_id"] == app.client_id
3790 assert returned["name"] == app.client_name
3794 describe "DELETE /api/pleroma/admin/oauth_app/:id" do
3795 test "with id", %{conn: conn} do
3796 app = insert(:oauth_app)
3800 |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
3801 |> json_response(:no_content)
3803 assert response == ""
3806 test "with non existance id", %{conn: conn} do
3809 |> delete("/api/pleroma/admin/oauth_app/0")
3810 |> json_response(:bad_request)
3812 assert response == ""
3816 describe "PATCH /api/pleroma/admin/oauth_app/:id" do
3817 test "with id", %{conn: conn} do
3818 app = insert(:oauth_app)
3820 name = "another name"
3821 url = "https://example.com"
3824 website = "http://website.com"
3828 |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
3835 |> json_response(200)
3839 "client_secret" => _,
3842 "redirect_uri" => ^url,
3844 "website" => ^website
3848 test "without id", %{conn: conn} do
3851 |> patch("/api/pleroma/admin/oauth_app/0")
3852 |> json_response(:bad_request)
3854 assert response == ""
3859 # Needed for testing
3860 defmodule Pleroma.Web.Endpoint.NotReal do
3863 defmodule Pleroma.Captcha.NotReal do