1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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
11 alias Pleroma.ModerationLog
13 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.UserInviteToken
16 alias Pleroma.Web.ActivityPub.Relay
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MediaProxy
19 import Pleroma.Factory
22 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
27 describe "DELETE /api/pleroma/admin/users" do
29 admin = insert(:user, is_admin: true)
34 |> assign(:user, admin)
35 |> put_req_header("accept", "application/json")
36 |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
38 log_entry = Repo.one(ModerationLog)
40 assert ModerationLog.get_log_entry_message(log_entry) ==
41 "@#{admin.nickname} deleted users: @#{user.nickname}"
43 assert json_response(conn, 200) == user.nickname
46 test "multiple users" do
47 admin = insert(:user, is_admin: true)
48 user_one = insert(:user)
49 user_two = insert(:user)
53 |> assign(:user, admin)
54 |> put_req_header("accept", "application/json")
55 |> delete("/api/pleroma/admin/users", %{
56 nicknames: [user_one.nickname, user_two.nickname]
59 log_entry = Repo.one(ModerationLog)
61 assert ModerationLog.get_log_entry_message(log_entry) ==
62 "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
64 response = json_response(conn, 200)
65 assert response -- [user_one.nickname, user_two.nickname] == []
69 describe "/api/pleroma/admin/users" do
71 admin = insert(:user, is_admin: true)
75 |> assign(:user, admin)
76 |> put_req_header("accept", "application/json")
77 |> post("/api/pleroma/admin/users", %{
81 "email" => "lain@example.org",
85 "nickname" => "lain2",
86 "email" => "lain2@example.org",
92 response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
93 assert response == ["success", "success"]
95 log_entry = Repo.one(ModerationLog)
97 assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
100 test "Cannot create user with exisiting email" do
101 admin = insert(:user, is_admin: true)
106 |> assign(:user, admin)
107 |> put_req_header("accept", "application/json")
108 |> post("/api/pleroma/admin/users", %{
111 "nickname" => "lain",
112 "email" => user.email,
118 assert json_response(conn, 409) == [
122 "email" => user.email,
125 "error" => "email has already been taken",
131 test "Cannot create user with exisiting nickname" do
132 admin = insert(:user, is_admin: true)
137 |> assign(:user, admin)
138 |> put_req_header("accept", "application/json")
139 |> post("/api/pleroma/admin/users", %{
142 "nickname" => user.nickname,
143 "email" => "someuser@plerama.social",
149 assert json_response(conn, 409) == [
153 "email" => "someuser@plerama.social",
154 "nickname" => user.nickname
156 "error" => "nickname has already been taken",
162 test "Multiple user creation works in transaction" do
163 admin = insert(:user, is_admin: true)
168 |> assign(:user, admin)
169 |> put_req_header("accept", "application/json")
170 |> post("/api/pleroma/admin/users", %{
173 "nickname" => "newuser",
174 "email" => "newuser@pleroma.social",
178 "nickname" => "lain",
179 "email" => user.email,
185 assert json_response(conn, 409) == [
189 "email" => user.email,
192 "error" => "email has already been taken",
198 "email" => "newuser@pleroma.social",
199 "nickname" => "newuser"
206 assert User.get_by_nickname("newuser") === nil
210 describe "/api/pleroma/admin/users/:nickname" do
211 test "Show", %{conn: conn} do
212 admin = insert(:user, is_admin: true)
217 |> assign(:user, admin)
218 |> get("/api/pleroma/admin/users/#{user.nickname}")
221 "deactivated" => false,
222 "id" => to_string(user.id),
224 "nickname" => user.nickname,
225 "roles" => %{"admin" => false, "moderator" => false},
227 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
228 "display_name" => HTML.strip_tags(user.name || user.nickname),
229 "confirmation_pending" => false
232 assert expected == json_response(conn, 200)
235 test "when the user doesn't exist", %{conn: conn} do
236 admin = insert(:user, is_admin: true)
241 |> assign(:user, admin)
242 |> get("/api/pleroma/admin/users/#{user.nickname}")
244 assert "Not found" == json_response(conn, 404)
248 describe "/api/pleroma/admin/users/follow" do
249 test "allows to force-follow another user" do
250 admin = insert(:user, is_admin: true)
252 follower = insert(:user)
255 |> assign(:user, admin)
256 |> put_req_header("accept", "application/json")
257 |> post("/api/pleroma/admin/users/follow", %{
258 "follower" => follower.nickname,
259 "followed" => user.nickname
262 user = User.get_cached_by_id(user.id)
263 follower = User.get_cached_by_id(follower.id)
265 assert User.following?(follower, user)
267 log_entry = Repo.one(ModerationLog)
269 assert ModerationLog.get_log_entry_message(log_entry) ==
270 "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
274 describe "/api/pleroma/admin/users/unfollow" do
275 test "allows to force-unfollow another user" do
276 admin = insert(:user, is_admin: true)
278 follower = insert(:user)
280 User.follow(follower, user)
283 |> assign(:user, admin)
284 |> put_req_header("accept", "application/json")
285 |> post("/api/pleroma/admin/users/unfollow", %{
286 "follower" => follower.nickname,
287 "followed" => user.nickname
290 user = User.get_cached_by_id(user.id)
291 follower = User.get_cached_by_id(follower.id)
293 refute User.following?(follower, user)
295 log_entry = Repo.one(ModerationLog)
297 assert ModerationLog.get_log_entry_message(log_entry) ==
298 "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
302 describe "PUT /api/pleroma/admin/users/tag" do
304 admin = insert(:user, is_admin: true)
305 user1 = insert(:user, %{tags: ["x"]})
306 user2 = insert(:user, %{tags: ["y"]})
307 user3 = insert(:user, %{tags: ["unchanged"]})
311 |> assign(:user, admin)
312 |> put_req_header("accept", "application/json")
314 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{
316 }&tags[]=foo&tags[]=bar"
319 %{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
322 test "it appends specified tags to users with specified nicknames", %{
328 assert json_response(conn, :no_content)
329 assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
330 assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
332 log_entry = Repo.one(ModerationLog)
335 [user1.nickname, user2.nickname]
336 |> Enum.map(&"@#{&1}")
339 tags = ["foo", "bar"] |> Enum.join(", ")
341 assert ModerationLog.get_log_entry_message(log_entry) ==
342 "@#{admin.nickname} added tags: #{tags} to users: #{users}"
345 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
346 assert json_response(conn, :no_content)
347 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
351 describe "DELETE /api/pleroma/admin/users/tag" do
353 admin = insert(:user, is_admin: true)
354 user1 = insert(:user, %{tags: ["x"]})
355 user2 = insert(:user, %{tags: ["y", "z"]})
356 user3 = insert(:user, %{tags: ["unchanged"]})
360 |> assign(:user, admin)
361 |> put_req_header("accept", "application/json")
363 "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{
368 %{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
371 test "it removes specified tags from users with specified nicknames", %{
377 assert json_response(conn, :no_content)
378 assert User.get_cached_by_id(user1.id).tags == []
379 assert User.get_cached_by_id(user2.id).tags == ["y"]
381 log_entry = Repo.one(ModerationLog)
384 [user1.nickname, user2.nickname]
385 |> Enum.map(&"@#{&1}")
388 tags = ["x", "z"] |> Enum.join(", ")
390 assert ModerationLog.get_log_entry_message(log_entry) ==
391 "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
394 test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
395 assert json_response(conn, :no_content)
396 assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
400 describe "/api/pleroma/admin/users/:nickname/permission_group" do
401 test "GET is giving user_info" do
402 admin = insert(:user, is_admin: true)
406 |> assign(:user, admin)
407 |> put_req_header("accept", "application/json")
408 |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
410 assert json_response(conn, 200) == %{
412 "is_moderator" => false
416 test "/:right POST, can add to a permission group" do
417 admin = insert(:user, is_admin: true)
422 |> assign(:user, admin)
423 |> put_req_header("accept", "application/json")
424 |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
426 assert json_response(conn, 200) == %{
430 log_entry = Repo.one(ModerationLog)
432 assert ModerationLog.get_log_entry_message(log_entry) ==
433 "@#{admin.nickname} made @#{user.nickname} admin"
436 test "/:right POST, can add to a permission group (multiple)" do
437 admin = insert(:user, is_admin: true)
438 user_one = insert(:user)
439 user_two = insert(:user)
443 |> assign(:user, admin)
444 |> put_req_header("accept", "application/json")
445 |> post("/api/pleroma/admin/users/permission_group/admin", %{
446 nicknames: [user_one.nickname, user_two.nickname]
449 assert json_response(conn, 200) == %{
453 log_entry = Repo.one(ModerationLog)
455 assert ModerationLog.get_log_entry_message(log_entry) ==
456 "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
459 test "/:right DELETE, can remove from a permission group" do
460 admin = insert(:user, is_admin: true)
461 user = insert(:user, is_admin: true)
465 |> assign(:user, admin)
466 |> put_req_header("accept", "application/json")
467 |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
469 assert json_response(conn, 200) == %{
473 log_entry = Repo.one(ModerationLog)
475 assert ModerationLog.get_log_entry_message(log_entry) ==
476 "@#{admin.nickname} revoked admin role from @#{user.nickname}"
479 test "/:right DELETE, can remove from a permission group (multiple)" do
480 admin = insert(:user, is_admin: true)
481 user_one = insert(:user, is_admin: true)
482 user_two = insert(:user, is_admin: true)
486 |> assign(:user, admin)
487 |> put_req_header("accept", "application/json")
488 |> delete("/api/pleroma/admin/users/permission_group/admin", %{
489 nicknames: [user_one.nickname, user_two.nickname]
492 assert json_response(conn, 200) == %{
496 log_entry = Repo.one(ModerationLog)
498 assert ModerationLog.get_log_entry_message(log_entry) ==
499 "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
505 describe "POST /api/pleroma/admin/email_invite, with valid config" do
507 [user: insert(:user, is_admin: true)]
510 clear_config([:instance, :registrations_open]) do
511 Pleroma.Config.put([:instance, :registrations_open], false)
514 clear_config([:instance, :invites_enabled]) do
515 Pleroma.Config.put([:instance, :invites_enabled], true)
518 test "sends invitation and returns 204", %{conn: conn, user: user} do
519 recipient_email = "foo@bar.com"
520 recipient_name = "J. D."
524 |> assign(:user, user)
526 "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
529 assert json_response(conn, :no_content)
531 token_record = List.last(Pleroma.Repo.all(Pleroma.UserInviteToken))
533 refute token_record.used
535 notify_email = Pleroma.Config.get([:instance, :notify_email])
536 instance_name = Pleroma.Config.get([:instance, :name])
539 Pleroma.Emails.UserEmail.user_invitation_email(
546 Swoosh.TestAssertions.assert_email_sent(
547 from: {instance_name, notify_email},
548 to: {recipient_name, recipient_email},
549 html_body: email.html_body
553 test "it returns 403 if requested by a non-admin", %{conn: conn} do
554 non_admin_user = insert(:user)
558 |> assign(:user, non_admin_user)
559 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
561 assert json_response(conn, :forbidden)
565 describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
567 [user: insert(:user, is_admin: true)]
570 clear_config([:instance, :registrations_open])
571 clear_config([:instance, :invites_enabled])
573 test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn, user: user} do
574 Pleroma.Config.put([:instance, :registrations_open], false)
575 Pleroma.Config.put([:instance, :invites_enabled], false)
579 |> assign(:user, user)
580 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
582 assert json_response(conn, :internal_server_error)
585 test "it returns 500 if `registrations_open` is enabled", %{conn: conn, user: user} do
586 Pleroma.Config.put([:instance, :registrations_open], true)
587 Pleroma.Config.put([:instance, :invites_enabled], true)
591 |> assign(:user, user)
592 |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
594 assert json_response(conn, :internal_server_error)
598 test "/api/pleroma/admin/users/:nickname/password_reset" do
599 admin = insert(:user, is_admin: true)
604 |> assign(:user, admin)
605 |> put_req_header("accept", "application/json")
606 |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
608 resp = json_response(conn, 200)
610 assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
613 describe "GET /api/pleroma/admin/users" do
615 admin = insert(:user, is_admin: true)
619 |> assign(:user, admin)
621 {:ok, conn: conn, admin: admin}
624 test "renders users array for the first page", %{conn: conn, admin: admin} do
625 user = insert(:user, local: false, tags: ["foo", "bar"])
626 conn = get(conn, "/api/pleroma/admin/users?page=1")
631 "deactivated" => admin.deactivated,
633 "nickname" => admin.nickname,
634 "roles" => %{"admin" => true, "moderator" => false},
637 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
638 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
639 "confirmation_pending" => false
642 "deactivated" => user.deactivated,
644 "nickname" => user.nickname,
645 "roles" => %{"admin" => false, "moderator" => false},
647 "tags" => ["foo", "bar"],
648 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
649 "display_name" => HTML.strip_tags(user.name || user.nickname),
650 "confirmation_pending" => false
653 |> Enum.sort_by(& &1["nickname"])
655 assert json_response(conn, 200) == %{
662 test "renders empty array for the second page", %{conn: conn} do
665 conn = get(conn, "/api/pleroma/admin/users?page=2")
667 assert json_response(conn, 200) == %{
674 test "regular search", %{conn: conn} do
675 user = insert(:user, nickname: "bob")
677 conn = get(conn, "/api/pleroma/admin/users?query=bo")
679 assert json_response(conn, 200) == %{
684 "deactivated" => user.deactivated,
686 "nickname" => user.nickname,
687 "roles" => %{"admin" => false, "moderator" => false},
690 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
691 "display_name" => HTML.strip_tags(user.name || user.nickname),
692 "confirmation_pending" => false
698 test "search by domain", %{conn: conn} do
699 user = insert(:user, nickname: "nickname@domain.com")
702 conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
704 assert json_response(conn, 200) == %{
709 "deactivated" => user.deactivated,
711 "nickname" => user.nickname,
712 "roles" => %{"admin" => false, "moderator" => false},
715 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
716 "display_name" => HTML.strip_tags(user.name || user.nickname),
717 "confirmation_pending" => false
723 test "search by full nickname", %{conn: conn} do
724 user = insert(:user, nickname: "nickname@domain.com")
727 conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
729 assert json_response(conn, 200) == %{
734 "deactivated" => user.deactivated,
736 "nickname" => user.nickname,
737 "roles" => %{"admin" => false, "moderator" => false},
740 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
741 "display_name" => HTML.strip_tags(user.name || user.nickname),
742 "confirmation_pending" => false
748 test "search by display name", %{conn: conn} do
749 user = insert(:user, name: "Display name")
752 conn = get(conn, "/api/pleroma/admin/users?name=display")
754 assert json_response(conn, 200) == %{
759 "deactivated" => user.deactivated,
761 "nickname" => user.nickname,
762 "roles" => %{"admin" => false, "moderator" => false},
765 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
766 "display_name" => HTML.strip_tags(user.name || user.nickname),
767 "confirmation_pending" => false
773 test "search by email", %{conn: conn} do
774 user = insert(:user, email: "email@example.com")
777 conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
779 assert json_response(conn, 200) == %{
784 "deactivated" => user.deactivated,
786 "nickname" => user.nickname,
787 "roles" => %{"admin" => false, "moderator" => false},
790 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
791 "display_name" => HTML.strip_tags(user.name || user.nickname),
792 "confirmation_pending" => false
798 test "regular search with page size", %{conn: conn} do
799 user = insert(:user, nickname: "aalice")
800 user2 = insert(:user, nickname: "alice")
802 conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
804 assert json_response(conn1, 200) == %{
809 "deactivated" => user.deactivated,
811 "nickname" => user.nickname,
812 "roles" => %{"admin" => false, "moderator" => false},
815 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
816 "display_name" => HTML.strip_tags(user.name || user.nickname),
817 "confirmation_pending" => false
822 conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
824 assert json_response(conn2, 200) == %{
829 "deactivated" => user2.deactivated,
831 "nickname" => user2.nickname,
832 "roles" => %{"admin" => false, "moderator" => false},
835 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
836 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
837 "confirmation_pending" => false
843 test "only local users" do
844 admin = insert(:user, is_admin: true, nickname: "john")
845 user = insert(:user, nickname: "bob")
847 insert(:user, nickname: "bobb", local: false)
851 |> assign(:user, admin)
852 |> get("/api/pleroma/admin/users?query=bo&filters=local")
854 assert json_response(conn, 200) == %{
859 "deactivated" => user.deactivated,
861 "nickname" => user.nickname,
862 "roles" => %{"admin" => false, "moderator" => false},
865 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
866 "display_name" => HTML.strip_tags(user.name || user.nickname),
867 "confirmation_pending" => false
873 test "only local users with no query", %{admin: old_admin} do
874 admin = insert(:user, is_admin: true, nickname: "john")
875 user = insert(:user, nickname: "bob")
877 insert(:user, nickname: "bobb", local: false)
881 |> assign(:user, admin)
882 |> get("/api/pleroma/admin/users?filters=local")
887 "deactivated" => user.deactivated,
889 "nickname" => user.nickname,
890 "roles" => %{"admin" => false, "moderator" => false},
893 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
894 "display_name" => HTML.strip_tags(user.name || user.nickname),
895 "confirmation_pending" => false
898 "deactivated" => admin.deactivated,
900 "nickname" => admin.nickname,
901 "roles" => %{"admin" => true, "moderator" => false},
904 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
905 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
906 "confirmation_pending" => false
909 "deactivated" => false,
910 "id" => old_admin.id,
912 "nickname" => old_admin.nickname,
913 "roles" => %{"admin" => true, "moderator" => false},
915 "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
916 "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
917 "confirmation_pending" => false
920 |> Enum.sort_by(& &1["nickname"])
922 assert json_response(conn, 200) == %{
929 test "load only admins", %{conn: conn, admin: admin} do
930 second_admin = insert(:user, is_admin: true)
934 conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
939 "deactivated" => false,
941 "nickname" => admin.nickname,
942 "roles" => %{"admin" => true, "moderator" => false},
943 "local" => admin.local,
945 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
946 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
947 "confirmation_pending" => false
950 "deactivated" => false,
951 "id" => second_admin.id,
952 "nickname" => second_admin.nickname,
953 "roles" => %{"admin" => true, "moderator" => false},
954 "local" => second_admin.local,
956 "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
957 "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
958 "confirmation_pending" => false
961 |> Enum.sort_by(& &1["nickname"])
963 assert json_response(conn, 200) == %{
970 test "load only moderators", %{conn: conn} do
971 moderator = insert(:user, is_moderator: true)
975 conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
977 assert json_response(conn, 200) == %{
982 "deactivated" => false,
983 "id" => moderator.id,
984 "nickname" => moderator.nickname,
985 "roles" => %{"admin" => false, "moderator" => true},
986 "local" => moderator.local,
988 "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
989 "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
990 "confirmation_pending" => false
996 test "load users with tags list", %{conn: conn} do
997 user1 = insert(:user, tags: ["first"])
998 user2 = insert(:user, tags: ["second"])
1002 conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
1007 "deactivated" => false,
1009 "nickname" => user1.nickname,
1010 "roles" => %{"admin" => false, "moderator" => false},
1011 "local" => user1.local,
1012 "tags" => ["first"],
1013 "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
1014 "display_name" => HTML.strip_tags(user1.name || user1.nickname),
1015 "confirmation_pending" => false
1018 "deactivated" => false,
1020 "nickname" => user2.nickname,
1021 "roles" => %{"admin" => false, "moderator" => false},
1022 "local" => user2.local,
1023 "tags" => ["second"],
1024 "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
1025 "display_name" => HTML.strip_tags(user2.name || user2.nickname),
1026 "confirmation_pending" => false
1029 |> Enum.sort_by(& &1["nickname"])
1031 assert json_response(conn, 200) == %{
1038 test "it works with multiple filters" do
1039 admin = insert(:user, nickname: "john", is_admin: true)
1040 user = insert(:user, nickname: "bob", local: false, deactivated: true)
1042 insert(:user, nickname: "ken", local: true, deactivated: true)
1043 insert(:user, nickname: "bobb", local: false, deactivated: false)
1047 |> assign(:user, admin)
1048 |> get("/api/pleroma/admin/users?filters=deactivated,external")
1050 assert json_response(conn, 200) == %{
1055 "deactivated" => user.deactivated,
1057 "nickname" => user.nickname,
1058 "roles" => %{"admin" => false, "moderator" => false},
1059 "local" => user.local,
1061 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1062 "display_name" => HTML.strip_tags(user.name || user.nickname),
1063 "confirmation_pending" => false
1069 test "it omits relay user", %{admin: admin} do
1070 assert %User{} = Relay.get_actor()
1074 |> assign(:user, admin)
1075 |> get("/api/pleroma/admin/users")
1077 assert json_response(conn, 200) == %{
1082 "deactivated" => admin.deactivated,
1084 "nickname" => admin.nickname,
1085 "roles" => %{"admin" => true, "moderator" => false},
1088 "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
1089 "display_name" => HTML.strip_tags(admin.name || admin.nickname),
1090 "confirmation_pending" => false
1097 test "PATCH /api/pleroma/admin/users/activate" do
1098 admin = insert(:user, is_admin: true)
1099 user_one = insert(:user, deactivated: true)
1100 user_two = insert(:user, deactivated: true)
1104 |> assign(:user, admin)
1106 "/api/pleroma/admin/users/activate",
1107 %{nicknames: [user_one.nickname, user_two.nickname]}
1110 response = json_response(conn, 200)
1111 assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
1113 log_entry = Repo.one(ModerationLog)
1115 assert ModerationLog.get_log_entry_message(log_entry) ==
1116 "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
1119 test "PATCH /api/pleroma/admin/users/deactivate" do
1120 admin = insert(:user, is_admin: true)
1121 user_one = insert(:user, deactivated: false)
1122 user_two = insert(:user, deactivated: false)
1126 |> assign(:user, admin)
1128 "/api/pleroma/admin/users/deactivate",
1129 %{nicknames: [user_one.nickname, user_two.nickname]}
1132 response = json_response(conn, 200)
1133 assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
1135 log_entry = Repo.one(ModerationLog)
1137 assert ModerationLog.get_log_entry_message(log_entry) ==
1138 "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
1141 test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
1142 admin = insert(:user, is_admin: true)
1143 user = insert(:user)
1147 |> assign(:user, admin)
1148 |> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
1150 assert json_response(conn, 200) ==
1152 "deactivated" => !user.deactivated,
1154 "nickname" => user.nickname,
1155 "roles" => %{"admin" => false, "moderator" => false},
1158 "avatar" => User.avatar_url(user) |> MediaProxy.url(),
1159 "display_name" => HTML.strip_tags(user.name || user.nickname),
1160 "confirmation_pending" => false
1163 log_entry = Repo.one(ModerationLog)
1165 assert ModerationLog.get_log_entry_message(log_entry) ==
1166 "@#{admin.nickname} deactivated users: @#{user.nickname}"
1169 describe "POST /api/pleroma/admin/users/invite_token" do
1171 admin = insert(:user, is_admin: true)
1175 |> assign(:user, admin)
1180 test "without options", %{conn: conn} do
1181 conn = post(conn, "/api/pleroma/admin/users/invite_token")
1183 invite_json = json_response(conn, 200)
1184 invite = UserInviteToken.find_by_token!(invite_json["token"])
1186 refute invite.expires_at
1187 refute invite.max_use
1188 assert invite.invite_type == "one_time"
1191 test "with expires_at", %{conn: conn} do
1193 post(conn, "/api/pleroma/admin/users/invite_token", %{
1194 "expires_at" => Date.to_string(Date.utc_today())
1197 invite_json = json_response(conn, 200)
1198 invite = UserInviteToken.find_by_token!(invite_json["token"])
1201 assert invite.expires_at == Date.utc_today()
1202 refute invite.max_use
1203 assert invite.invite_type == "date_limited"
1206 test "with max_use", %{conn: conn} do
1207 conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
1209 invite_json = json_response(conn, 200)
1210 invite = UserInviteToken.find_by_token!(invite_json["token"])
1212 refute invite.expires_at
1213 assert invite.max_use == 150
1214 assert invite.invite_type == "reusable"
1217 test "with max use and expires_at", %{conn: conn} do
1219 post(conn, "/api/pleroma/admin/users/invite_token", %{
1221 "expires_at" => Date.to_string(Date.utc_today())
1224 invite_json = json_response(conn, 200)
1225 invite = UserInviteToken.find_by_token!(invite_json["token"])
1227 assert invite.expires_at == Date.utc_today()
1228 assert invite.max_use == 150
1229 assert invite.invite_type == "reusable_date_limited"
1233 describe "GET /api/pleroma/admin/users/invites" do
1235 admin = insert(:user, is_admin: true)
1239 |> assign(:user, admin)
1244 test "no invites", %{conn: conn} do
1245 conn = get(conn, "/api/pleroma/admin/users/invites")
1247 assert json_response(conn, 200) == %{"invites" => []}
1250 test "with invite", %{conn: conn} do
1251 {:ok, invite} = UserInviteToken.create_invite()
1253 conn = get(conn, "/api/pleroma/admin/users/invites")
1255 assert json_response(conn, 200) == %{
1258 "expires_at" => nil,
1260 "invite_type" => "one_time",
1262 "token" => invite.token,
1271 describe "POST /api/pleroma/admin/users/revoke_invite" do
1272 test "with token" do
1273 admin = insert(:user, is_admin: true)
1274 {:ok, invite} = UserInviteToken.create_invite()
1278 |> assign(:user, admin)
1279 |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
1281 assert json_response(conn, 200) == %{
1282 "expires_at" => nil,
1284 "invite_type" => "one_time",
1286 "token" => invite.token,
1292 test "with invalid token" do
1293 admin = insert(:user, is_admin: true)
1297 |> assign(:user, admin)
1298 |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
1300 assert json_response(conn, :not_found) == "Not found"
1304 describe "GET /api/pleroma/admin/reports/:id" do
1305 setup %{conn: conn} do
1306 admin = insert(:user, is_admin: true)
1308 %{conn: assign(conn, :user, admin)}
1311 test "returns report by its id", %{conn: conn} do
1312 [reporter, target_user] = insert_pair(:user)
1313 activity = insert(:note_activity, user: target_user)
1315 {:ok, %{id: report_id}} =
1316 CommonAPI.report(reporter, %{
1317 "account_id" => target_user.id,
1318 "comment" => "I feel offended",
1319 "status_ids" => [activity.id]
1324 |> get("/api/pleroma/admin/reports/#{report_id}")
1325 |> json_response(:ok)
1327 assert response["id"] == report_id
1330 test "returns 404 when report id is invalid", %{conn: conn} do
1331 conn = get(conn, "/api/pleroma/admin/reports/test")
1333 assert json_response(conn, :not_found) == "Not found"
1337 describe "PATCH /api/pleroma/admin/reports" do
1338 setup %{conn: conn} do
1339 admin = insert(:user, is_admin: true)
1340 [reporter, target_user] = insert_pair(:user)
1341 activity = insert(:note_activity, user: target_user)
1343 {:ok, %{id: report_id}} =
1344 CommonAPI.report(reporter, %{
1345 "account_id" => target_user.id,
1346 "comment" => "I feel offended",
1347 "status_ids" => [activity.id]
1350 {:ok, %{id: second_report_id}} =
1351 CommonAPI.report(reporter, %{
1352 "account_id" => target_user.id,
1353 "comment" => "I feel very offended",
1354 "status_ids" => [activity.id]
1358 conn: assign(conn, :user, admin),
1361 second_report_id: second_report_id
1365 test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
1367 |> patch("/api/pleroma/admin/reports", %{
1369 %{"state" => "resolved", "id" => id}
1372 |> json_response(:no_content)
1374 activity = Activity.get_by_id(id)
1375 assert activity.data["state"] == "resolved"
1377 log_entry = Repo.one(ModerationLog)
1379 assert ModerationLog.get_log_entry_message(log_entry) ==
1380 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1383 test "closes report", %{conn: conn, id: id, admin: admin} do
1385 |> patch("/api/pleroma/admin/reports", %{
1387 %{"state" => "closed", "id" => id}
1390 |> json_response(:no_content)
1392 activity = Activity.get_by_id(id)
1393 assert activity.data["state"] == "closed"
1395 log_entry = Repo.one(ModerationLog)
1397 assert ModerationLog.get_log_entry_message(log_entry) ==
1398 "@#{admin.nickname} updated report ##{id} with 'closed' state"
1401 test "returns 400 when state is unknown", %{conn: conn, id: id} do
1404 |> patch("/api/pleroma/admin/reports", %{
1406 %{"state" => "test", "id" => id}
1410 assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
1413 test "returns 404 when report is not exist", %{conn: conn} do
1416 |> patch("/api/pleroma/admin/reports", %{
1418 %{"state" => "closed", "id" => "test"}
1422 assert hd(json_response(conn, :bad_request))["error"] == "not_found"
1425 test "updates state of multiple reports", %{
1429 second_report_id: second_report_id
1432 |> patch("/api/pleroma/admin/reports", %{
1434 %{"state" => "resolved", "id" => id},
1435 %{"state" => "closed", "id" => second_report_id}
1438 |> json_response(:no_content)
1440 activity = Activity.get_by_id(id)
1441 second_activity = Activity.get_by_id(second_report_id)
1442 assert activity.data["state"] == "resolved"
1443 assert second_activity.data["state"] == "closed"
1445 [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
1447 assert ModerationLog.get_log_entry_message(first_log_entry) ==
1448 "@#{admin.nickname} updated report ##{id} with 'resolved' state"
1450 assert ModerationLog.get_log_entry_message(second_log_entry) ==
1451 "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
1455 describe "GET /api/pleroma/admin/reports" do
1456 setup %{conn: conn} do
1457 admin = insert(:user, is_admin: true)
1459 %{conn: assign(conn, :user, admin)}
1462 test "returns empty response when no reports created", %{conn: conn} do
1465 |> get("/api/pleroma/admin/reports")
1466 |> json_response(:ok)
1468 assert Enum.empty?(response["reports"])
1469 assert response["total"] == 0
1472 test "returns reports", %{conn: conn} do
1473 [reporter, target_user] = insert_pair(:user)
1474 activity = insert(:note_activity, user: target_user)
1476 {:ok, %{id: report_id}} =
1477 CommonAPI.report(reporter, %{
1478 "account_id" => target_user.id,
1479 "comment" => "I feel offended",
1480 "status_ids" => [activity.id]
1485 |> get("/api/pleroma/admin/reports")
1486 |> json_response(:ok)
1488 [report] = response["reports"]
1490 assert length(response["reports"]) == 1
1491 assert report["id"] == report_id
1493 assert response["total"] == 1
1496 test "returns reports with specified state", %{conn: conn} do
1497 [reporter, target_user] = insert_pair(:user)
1498 activity = insert(:note_activity, user: target_user)
1500 {:ok, %{id: first_report_id}} =
1501 CommonAPI.report(reporter, %{
1502 "account_id" => target_user.id,
1503 "comment" => "I feel offended",
1504 "status_ids" => [activity.id]
1507 {:ok, %{id: second_report_id}} =
1508 CommonAPI.report(reporter, %{
1509 "account_id" => target_user.id,
1510 "comment" => "I don't like this user"
1513 CommonAPI.update_report_state(second_report_id, "closed")
1517 |> get("/api/pleroma/admin/reports", %{
1520 |> json_response(:ok)
1522 [open_report] = response["reports"]
1524 assert length(response["reports"]) == 1
1525 assert open_report["id"] == first_report_id
1527 assert response["total"] == 1
1531 |> get("/api/pleroma/admin/reports", %{
1534 |> json_response(:ok)
1536 [closed_report] = response["reports"]
1538 assert length(response["reports"]) == 1
1539 assert closed_report["id"] == second_report_id
1541 assert response["total"] == 1
1545 |> get("/api/pleroma/admin/reports", %{
1546 "state" => "resolved"
1548 |> json_response(:ok)
1550 assert Enum.empty?(response["reports"])
1551 assert response["total"] == 0
1554 test "returns 403 when requested by a non-admin" do
1555 user = insert(:user)
1559 |> assign(:user, user)
1560 |> get("/api/pleroma/admin/reports")
1562 assert json_response(conn, :forbidden) ==
1563 %{"error" => "User is not an admin or OAuth admin scope is not granted."}
1566 test "returns 403 when requested by anonymous" do
1569 |> get("/api/pleroma/admin/reports")
1571 assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
1575 describe "GET /api/pleroma/admin/grouped_reports" do
1576 setup %{conn: conn} do
1577 admin = insert(:user, is_admin: true)
1578 [reporter, target_user] = insert_pair(:user)
1580 date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
1581 date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
1582 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
1585 insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
1588 insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
1591 insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
1593 {:ok, first_report} =
1594 CommonAPI.report(reporter, %{
1595 "account_id" => target_user.id,
1596 "status_ids" => [first_status.id, second_status.id, third_status.id]
1599 {:ok, second_report} =
1600 CommonAPI.report(reporter, %{
1601 "account_id" => target_user.id,
1602 "status_ids" => [first_status.id, second_status.id]
1605 {:ok, third_report} =
1606 CommonAPI.report(reporter, %{
1607 "account_id" => target_user.id,
1608 "status_ids" => [first_status.id]
1612 conn: assign(conn, :user, admin),
1613 first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
1614 second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
1615 third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
1616 first_status_reports: [first_report, second_report, third_report],
1617 second_status_reports: [first_report, second_report],
1618 third_status_reports: [first_report],
1619 target_user: target_user,
1624 test "returns reports grouped by status", %{
1626 first_status: first_status,
1627 second_status: second_status,
1628 third_status: third_status,
1629 first_status_reports: first_status_reports,
1630 second_status_reports: second_status_reports,
1631 third_status_reports: third_status_reports,
1632 target_user: target_user,
1637 |> get("/api/pleroma/admin/grouped_reports")
1638 |> json_response(:ok)
1640 assert length(response["reports"]) == 3
1643 Enum.find(response["reports"], &(&1["status"]["id"] == first_status.data["id"]))
1646 Enum.find(response["reports"], &(&1["status"]["id"] == second_status.data["id"]))
1649 Enum.find(response["reports"], &(&1["status"]["id"] == third_status.data["id"]))
1651 assert length(first_group["reports"]) == 3
1652 assert length(second_group["reports"]) == 2
1653 assert length(third_group["reports"]) == 1
1655 assert first_group["date"] ==
1656 Enum.max_by(first_status_reports, fn act ->
1657 NaiveDateTime.from_iso8601!(act.data["published"])
1658 end).data["published"]
1660 assert first_group["status"] == %{
1661 "id" => first_status.data["id"],
1662 "content" => first_status.object.data["content"],
1663 "published" => first_status.object.data["published"]
1666 assert first_group["account"]["id"] == target_user.id
1668 assert length(first_group["actors"]) == 1
1669 assert hd(first_group["actors"])["id"] == reporter.id
1671 assert Enum.map(first_group["reports"], & &1["id"]) --
1672 Enum.map(first_status_reports, & &1.id) == []
1674 assert second_group["date"] ==
1675 Enum.max_by(second_status_reports, fn act ->
1676 NaiveDateTime.from_iso8601!(act.data["published"])
1677 end).data["published"]
1679 assert second_group["status"] == %{
1680 "id" => second_status.data["id"],
1681 "content" => second_status.object.data["content"],
1682 "published" => second_status.object.data["published"]
1685 assert second_group["account"]["id"] == target_user.id
1687 assert length(second_group["actors"]) == 1
1688 assert hd(second_group["actors"])["id"] == reporter.id
1690 assert Enum.map(second_group["reports"], & &1["id"]) --
1691 Enum.map(second_status_reports, & &1.id) == []
1693 assert third_group["date"] ==
1694 Enum.max_by(third_status_reports, fn act ->
1695 NaiveDateTime.from_iso8601!(act.data["published"])
1696 end).data["published"]
1698 assert third_group["status"] == %{
1699 "id" => third_status.data["id"],
1700 "content" => third_status.object.data["content"],
1701 "published" => third_status.object.data["published"]
1704 assert third_group["account"]["id"] == target_user.id
1706 assert length(third_group["actors"]) == 1
1707 assert hd(third_group["actors"])["id"] == reporter.id
1709 assert Enum.map(third_group["reports"], & &1["id"]) --
1710 Enum.map(third_status_reports, & &1.id) == []
1714 describe "POST /api/pleroma/admin/reports/:id/respond" do
1715 setup %{conn: conn} do
1716 admin = insert(:user, is_admin: true)
1718 %{conn: assign(conn, :user, admin), admin: admin}
1721 test "returns created dm", %{conn: conn, admin: admin} do
1722 [reporter, target_user] = insert_pair(:user)
1723 activity = insert(:note_activity, user: target_user)
1725 {:ok, %{id: report_id}} =
1726 CommonAPI.report(reporter, %{
1727 "account_id" => target_user.id,
1728 "comment" => "I feel offended",
1729 "status_ids" => [activity.id]
1734 |> post("/api/pleroma/admin/reports/#{report_id}/respond", %{
1735 "status" => "I will check it out"
1737 |> json_response(:ok)
1739 recipients = Enum.map(response["mentions"], & &1["username"])
1741 assert reporter.nickname in recipients
1742 assert response["content"] == "I will check it out"
1743 assert response["visibility"] == "direct"
1745 log_entry = Repo.one(ModerationLog)
1747 assert ModerationLog.get_log_entry_message(log_entry) ==
1748 "@#{admin.nickname} responded with 'I will check it out' to report ##{
1753 test "returns 400 when status is missing", %{conn: conn} do
1754 conn = post(conn, "/api/pleroma/admin/reports/test/respond")
1756 assert json_response(conn, :bad_request) == "Invalid parameters"
1759 test "returns 404 when report id is invalid", %{conn: conn} do
1761 post(conn, "/api/pleroma/admin/reports/test/respond", %{
1765 assert json_response(conn, :not_found) == "Not found"
1769 describe "PUT /api/pleroma/admin/statuses/:id" do
1770 setup %{conn: conn} do
1771 admin = insert(:user, is_admin: true)
1772 activity = insert(:note_activity)
1774 %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
1777 test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
1780 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
1781 |> json_response(:ok)
1783 assert response["sensitive"]
1785 log_entry = Repo.one(ModerationLog)
1787 assert ModerationLog.get_log_entry_message(log_entry) ==
1788 "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
1792 |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
1793 |> json_response(:ok)
1795 refute response["sensitive"]
1798 test "change visibility flag", %{conn: conn, id: id, admin: admin} do
1801 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
1802 |> json_response(:ok)
1804 assert response["visibility"] == "public"
1806 log_entry = Repo.one(ModerationLog)
1808 assert ModerationLog.get_log_entry_message(log_entry) ==
1809 "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
1813 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
1814 |> json_response(:ok)
1816 assert response["visibility"] == "private"
1820 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
1821 |> json_response(:ok)
1823 assert response["visibility"] == "unlisted"
1826 test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
1829 |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})
1831 assert json_response(conn, :bad_request) == "Unsupported visibility"
1835 describe "DELETE /api/pleroma/admin/statuses/:id" do
1836 setup %{conn: conn} do
1837 admin = insert(:user, is_admin: true)
1838 activity = insert(:note_activity)
1840 %{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
1843 test "deletes status", %{conn: conn, id: id, admin: admin} do
1845 |> delete("/api/pleroma/admin/statuses/#{id}")
1846 |> json_response(:ok)
1848 refute Activity.get_by_id(id)
1850 log_entry = Repo.one(ModerationLog)
1852 assert ModerationLog.get_log_entry_message(log_entry) ==
1853 "@#{admin.nickname} deleted status ##{id}"
1856 test "returns error when status is not exist", %{conn: conn} do
1859 |> delete("/api/pleroma/admin/statuses/test")
1861 assert json_response(conn, :bad_request) == "Could not delete"
1865 describe "GET /api/pleroma/admin/config" do
1866 setup %{conn: conn} do
1867 admin = insert(:user, is_admin: true)
1869 %{conn: assign(conn, :user, admin)}
1872 test "without any settings in db", %{conn: conn} do
1873 conn = get(conn, "/api/pleroma/admin/config")
1875 assert json_response(conn, 200) == %{"configs" => []}
1878 test "with settings in db", %{conn: conn} do
1879 config1 = insert(:config)
1880 config2 = insert(:config)
1882 conn = get(conn, "/api/pleroma/admin/config")
1895 } = json_response(conn, 200)
1897 assert key1 == config1.key
1898 assert key2 == config2.key
1902 describe "POST /api/pleroma/admin/config" do
1903 setup %{conn: conn} do
1904 admin = insert(:user, is_admin: true)
1906 temp_file = "config/test.exported_from_db.secret.exs"
1909 Application.delete_env(:pleroma, :key1)
1910 Application.delete_env(:pleroma, :key2)
1911 Application.delete_env(:pleroma, :key3)
1912 Application.delete_env(:pleroma, :key4)
1913 Application.delete_env(:pleroma, :keyaa1)
1914 Application.delete_env(:pleroma, :keyaa2)
1915 Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
1916 Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
1917 :ok = File.rm(temp_file)
1920 %{conn: assign(conn, :user, admin)}
1923 clear_config([:instance, :dynamic_configuration]) do
1924 Pleroma.Config.put([:instance, :dynamic_configuration], true)
1927 @tag capture_log: true
1928 test "create new config setting in db", %{conn: conn} do
1930 post(conn, "/api/pleroma/admin/config", %{
1932 %{group: "pleroma", key: "key1", value: "value1"},
1935 key: "Ueberauth.Strategy.Twitter.OAuth",
1936 value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
1942 ":nested_1" => "nested_value1",
1944 %{":nested_22" => "nested_value222"},
1945 %{":nested_33" => %{":nested_44" => "nested_444"}}
1953 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1954 %{"nested_4" => true}
1960 value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
1965 value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
1970 assert json_response(conn, 200) == %{
1973 "group" => "pleroma",
1978 "group" => "ueberauth",
1979 "key" => "Ueberauth.Strategy.Twitter.OAuth",
1980 "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}]
1983 "group" => "pleroma",
1986 ":nested_1" => "nested_value1",
1988 %{":nested_22" => "nested_value222"},
1989 %{":nested_33" => %{":nested_44" => "nested_444"}}
1994 "group" => "pleroma",
1997 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
1998 %{"nested_4" => true}
2002 "group" => "pleroma",
2004 "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}
2009 "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
2014 assert Application.get_env(:pleroma, :key1) == "value1"
2016 assert Application.get_env(:pleroma, :key2) == %{
2017 nested_1: "nested_value1",
2019 %{nested_22: "nested_value222"},
2020 %{nested_33: %{nested_44: "nested_444"}}
2024 assert Application.get_env(:pleroma, :key3) == [
2025 %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
2026 %{"nested_4" => true}
2029 assert Application.get_env(:pleroma, :key4) == %{
2030 "endpoint" => "https://example.com",
2034 assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
2037 test "update config setting & delete", %{conn: conn} do
2038 config1 = insert(:config, key: "keyaa1")
2039 config2 = insert(:config, key: "keyaa2")
2043 key: "Ueberauth.Strategy.Microsoft.OAuth",
2044 value: :erlang.term_to_binary([])
2048 post(conn, "/api/pleroma/admin/config", %{
2050 %{group: config1.group, key: config1.key, value: "another_value"},
2051 %{group: config2.group, key: config2.key, delete: "true"},
2054 key: "Ueberauth.Strategy.Microsoft.OAuth",
2060 assert json_response(conn, 200) == %{
2063 "group" => "pleroma",
2064 "key" => config1.key,
2065 "value" => "another_value"
2070 assert Application.get_env(:pleroma, :keyaa1) == "another_value"
2071 refute Application.get_env(:pleroma, :keyaa2)
2074 test "common config example", %{conn: conn} do
2076 post(conn, "/api/pleroma/admin/config", %{
2079 "group" => "pleroma",
2080 "key" => "Pleroma.Captcha.NotReal",
2082 %{"tuple" => [":enabled", false]},
2083 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2084 %{"tuple" => [":seconds_valid", 60]},
2085 %{"tuple" => [":path", ""]},
2086 %{"tuple" => [":key1", nil]},
2087 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2088 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
2089 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
2090 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
2091 %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}
2097 assert json_response(conn, 200) == %{
2100 "group" => "pleroma",
2101 "key" => "Pleroma.Captcha.NotReal",
2103 %{"tuple" => [":enabled", false]},
2104 %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
2105 %{"tuple" => [":seconds_valid", 60]},
2106 %{"tuple" => [":path", ""]},
2107 %{"tuple" => [":key1", nil]},
2108 %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
2109 %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
2110 %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
2111 %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
2112 %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}
2119 test "tuples with more than two values", %{conn: conn} do
2121 post(conn, "/api/pleroma/admin/config", %{
2124 "group" => "pleroma",
2125 "key" => "Pleroma.Web.Endpoint.NotReal",
2141 "/api/v1/streaming",
2142 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2149 "Phoenix.Endpoint.CowboyWebSocket",
2152 "Phoenix.Transports.WebSocket",
2155 "Pleroma.Web.Endpoint",
2156 "Pleroma.Web.UserSocket",
2167 "Phoenix.Endpoint.Cowboy2Handler",
2168 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2185 assert json_response(conn, 200) == %{
2188 "group" => "pleroma",
2189 "key" => "Pleroma.Web.Endpoint.NotReal",
2205 "/api/v1/streaming",
2206 "Pleroma.Web.MastodonAPI.WebsocketHandler",
2213 "Phoenix.Endpoint.CowboyWebSocket",
2216 "Phoenix.Transports.WebSocket",
2219 "Pleroma.Web.Endpoint",
2220 "Pleroma.Web.UserSocket",
2231 "Phoenix.Endpoint.Cowboy2Handler",
2232 %{"tuple" => ["Pleroma.Web.Endpoint", []]}
2250 test "settings with nesting map", %{conn: conn} do
2252 post(conn, "/api/pleroma/admin/config", %{
2255 "group" => "pleroma",
2258 %{"tuple" => [":key2", "some_val"]},
2263 ":max_options" => 20,
2264 ":max_option_chars" => 200,
2265 ":min_expiration" => 0,
2266 ":max_expiration" => 31_536_000,
2268 ":max_options" => 20,
2269 ":max_option_chars" => 200,
2270 ":min_expiration" => 0,
2271 ":max_expiration" => 31_536_000
2281 assert json_response(conn, 200) ==
2285 "group" => "pleroma",
2288 %{"tuple" => [":key2", "some_val"]},
2293 ":max_expiration" => 31_536_000,
2294 ":max_option_chars" => 200,
2295 ":max_options" => 20,
2296 ":min_expiration" => 0,
2298 ":max_expiration" => 31_536_000,
2299 ":max_option_chars" => 200,
2300 ":max_options" => 20,
2301 ":min_expiration" => 0
2312 test "value as map", %{conn: conn} do
2314 post(conn, "/api/pleroma/admin/config", %{
2317 "group" => "pleroma",
2319 "value" => %{"key" => "some_val"}
2324 assert json_response(conn, 200) ==
2328 "group" => "pleroma",
2330 "value" => %{"key" => "some_val"}
2336 test "dispatch setting", %{conn: conn} do
2338 post(conn, "/api/pleroma/admin/config", %{
2341 "group" => "pleroma",
2342 "key" => "Pleroma.Web.Endpoint.NotReal",
2348 %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
2349 %{"tuple" => [":dispatch", ["{:_,
2351 {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
2352 {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket,
2353 {Phoenix.Transports.WebSocket,
2354 {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}},
2355 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
2366 "{:_, [{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, " <>
2367 "{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, {Phoenix.Transports.WebSocket, " <>
2368 "{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, " <>
2369 "{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}]}"
2371 assert json_response(conn, 200) == %{
2374 "group" => "pleroma",
2375 "key" => "Pleroma.Web.Endpoint.NotReal",
2381 %{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]},
2399 test "queues key as atom", %{conn: conn} do
2401 post(conn, "/api/pleroma/admin/config", %{
2407 %{"tuple" => [":federator_incoming", 50]},
2408 %{"tuple" => [":federator_outgoing", 50]},
2409 %{"tuple" => [":web_push", 50]},
2410 %{"tuple" => [":mailer", 10]},
2411 %{"tuple" => [":transmogrifier", 20]},
2412 %{"tuple" => [":scheduled_activities", 10]},
2413 %{"tuple" => [":background", 5]}
2419 assert json_response(conn, 200) == %{
2425 %{"tuple" => [":federator_incoming", 50]},
2426 %{"tuple" => [":federator_outgoing", 50]},
2427 %{"tuple" => [":web_push", 50]},
2428 %{"tuple" => [":mailer", 10]},
2429 %{"tuple" => [":transmogrifier", 20]},
2430 %{"tuple" => [":scheduled_activities", 10]},
2431 %{"tuple" => [":background", 5]}
2438 test "delete part of settings by atom subkeys", %{conn: conn} do
2442 value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
2446 post(conn, "/api/pleroma/admin/config", %{
2449 group: config.group,
2451 subkeys: [":subkey1", ":subkey3"],
2458 json_response(conn, 200) == %{
2461 "group" => "pleroma",
2463 "value" => [%{"tuple" => [":subkey2", "val2"]}]
2471 describe "config mix tasks run" do
2472 setup %{conn: conn} do
2473 admin = insert(:user, is_admin: true)
2475 temp_file = "config/test.exported_from_db.secret.exs"
2477 Mix.shell(Mix.Shell.Quiet)
2480 Mix.shell(Mix.Shell.IO)
2481 :ok = File.rm(temp_file)
2484 %{conn: assign(conn, :user, admin), admin: admin}
2487 clear_config([:instance, :dynamic_configuration]) do
2488 Pleroma.Config.put([:instance, :dynamic_configuration], true)
2491 clear_config([:feed, :post_title]) do
2492 Pleroma.Config.put([:feed, :post_title], %{max_length: 100, omission: "…"})
2495 test "transfer settings to DB and to file", %{conn: conn, admin: admin} do
2496 assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
2497 conn = get(conn, "/api/pleroma/admin/config/migrate_to_db")
2498 assert json_response(conn, 200) == %{}
2499 assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) > 0
2503 |> assign(:user, admin)
2504 |> get("/api/pleroma/admin/config/migrate_from_db")
2506 assert json_response(conn, 200) == %{}
2507 assert Pleroma.Repo.all(Pleroma.Web.AdminAPI.Config) == []
2511 describe "GET /api/pleroma/admin/users/:nickname/statuses" do
2513 admin = insert(:user, is_admin: true)
2514 user = insert(:user)
2516 date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
2517 date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
2518 date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
2520 insert(:note_activity, user: user, published: date1)
2521 insert(:note_activity, user: user, published: date2)
2522 insert(:note_activity, user: user, published: date3)
2526 |> assign(:user, admin)
2528 {:ok, conn: conn, user: user}
2531 test "renders user's statuses", %{conn: conn, user: user} do
2532 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2534 assert json_response(conn, 200) |> length() == 3
2537 test "renders user's statuses with a limit", %{conn: conn, user: user} do
2538 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
2540 assert json_response(conn, 200) |> length() == 2
2543 test "doesn't return private statuses by default", %{conn: conn, user: user} do
2544 {:ok, _private_status} =
2545 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2547 {:ok, _public_status} =
2548 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2550 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
2552 assert json_response(conn, 200) |> length() == 4
2555 test "returns private statuses with godmode on", %{conn: conn, user: user} do
2556 {:ok, _private_status} =
2557 CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
2559 {:ok, _public_status} =
2560 CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
2562 conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
2564 assert json_response(conn, 200) |> length() == 5
2568 describe "GET /api/pleroma/admin/moderation_log" do
2569 setup %{conn: conn} do
2570 admin = insert(:user, is_admin: true)
2571 moderator = insert(:user, is_moderator: true)
2573 %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator}
2576 test "returns the log", %{conn: conn, admin: admin} do
2577 Repo.insert(%ModerationLog{
2581 "nickname" => admin.nickname,
2584 action: "relay_follow",
2585 target: "https://example.org/relay"
2587 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2590 Repo.insert(%ModerationLog{
2594 "nickname" => admin.nickname,
2597 action: "relay_unfollow",
2598 target: "https://example.org/relay"
2600 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2603 conn = get(conn, "/api/pleroma/admin/moderation_log")
2605 response = json_response(conn, 200)
2606 [first_entry, second_entry] = response["items"]
2608 assert response["total"] == 2
2609 assert first_entry["data"]["action"] == "relay_unfollow"
2611 assert first_entry["message"] ==
2612 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2614 assert second_entry["data"]["action"] == "relay_follow"
2616 assert second_entry["message"] ==
2617 "@#{admin.nickname} followed relay: https://example.org/relay"
2620 test "returns the log with pagination", %{conn: conn, admin: admin} do
2621 Repo.insert(%ModerationLog{
2625 "nickname" => admin.nickname,
2628 action: "relay_follow",
2629 target: "https://example.org/relay"
2631 inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
2634 Repo.insert(%ModerationLog{
2638 "nickname" => admin.nickname,
2641 action: "relay_unfollow",
2642 target: "https://example.org/relay"
2644 inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
2647 conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
2649 response1 = json_response(conn1, 200)
2650 [first_entry] = response1["items"]
2652 assert response1["total"] == 2
2653 assert response1["items"] |> length() == 1
2654 assert first_entry["data"]["action"] == "relay_unfollow"
2656 assert first_entry["message"] ==
2657 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2659 conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
2661 response2 = json_response(conn2, 200)
2662 [second_entry] = response2["items"]
2664 assert response2["total"] == 2
2665 assert response2["items"] |> length() == 1
2666 assert second_entry["data"]["action"] == "relay_follow"
2668 assert second_entry["message"] ==
2669 "@#{admin.nickname} followed relay: https://example.org/relay"
2672 test "filters log by date", %{conn: conn, admin: admin} do
2673 first_date = "2017-08-15T15:47:06Z"
2674 second_date = "2017-08-20T15:47:06Z"
2676 Repo.insert(%ModerationLog{
2680 "nickname" => admin.nickname,
2683 action: "relay_follow",
2684 target: "https://example.org/relay"
2686 inserted_at: NaiveDateTime.from_iso8601!(first_date)
2689 Repo.insert(%ModerationLog{
2693 "nickname" => admin.nickname,
2696 action: "relay_unfollow",
2697 target: "https://example.org/relay"
2699 inserted_at: NaiveDateTime.from_iso8601!(second_date)
2705 "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
2708 response1 = json_response(conn1, 200)
2709 [first_entry] = response1["items"]
2711 assert response1["total"] == 1
2712 assert first_entry["data"]["action"] == "relay_unfollow"
2714 assert first_entry["message"] ==
2715 "@#{admin.nickname} unfollowed relay: https://example.org/relay"
2718 test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
2719 Repo.insert(%ModerationLog{
2723 "nickname" => admin.nickname,
2726 action: "relay_follow",
2727 target: "https://example.org/relay"
2731 Repo.insert(%ModerationLog{
2734 "id" => moderator.id,
2735 "nickname" => moderator.nickname,
2738 action: "relay_unfollow",
2739 target: "https://example.org/relay"
2743 conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
2745 response1 = json_response(conn1, 200)
2746 [first_entry] = response1["items"]
2748 assert response1["total"] == 1
2749 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
2752 test "returns log filtered by search", %{conn: conn, moderator: moderator} do
2753 ModerationLog.insert_log(%{
2755 action: "relay_follow",
2756 target: "https://example.org/relay"
2759 ModerationLog.insert_log(%{
2761 action: "relay_unfollow",
2762 target: "https://example.org/relay"
2765 conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
2767 response1 = json_response(conn1, 200)
2768 [first_entry] = response1["items"]
2770 assert response1["total"] == 1
2772 assert get_in(first_entry, ["data", "message"]) ==
2773 "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
2777 describe "PATCH /users/:nickname/force_password_reset" do
2778 setup %{conn: conn} do
2779 admin = insert(:user, is_admin: true)
2780 user = insert(:user)
2782 %{conn: assign(conn, :user, admin), admin: admin, user: user}
2785 test "sets password_reset_pending to true", %{admin: admin, user: user} do
2786 assert user.password_reset_pending == false
2790 |> assign(:user, admin)
2791 |> patch("/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
2793 assert json_response(conn, 204) == ""
2795 ObanHelpers.perform_all()
2797 assert User.get_by_id(user.id).password_reset_pending == true
2801 describe "relays" do
2802 setup %{conn: conn} do
2803 admin = insert(:user, is_admin: true)
2805 %{conn: assign(conn, :user, admin), admin: admin}
2808 test "POST /relay", %{admin: admin} do
2811 |> assign(:user, admin)
2812 |> post("/api/pleroma/admin/relay", %{
2813 relay_url: "http://mastodon.example.org/users/admin"
2816 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2818 log_entry = Repo.one(ModerationLog)
2820 assert ModerationLog.get_log_entry_message(log_entry) ==
2821 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2824 test "GET /relay", %{admin: admin} do
2825 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
2827 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
2828 |> Enum.each(fn ap_id ->
2829 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
2830 User.follow(relay_user, user)
2835 |> assign(:user, admin)
2836 |> get("/api/pleroma/admin/relay")
2838 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
2841 test "DELETE /relay", %{admin: admin} do
2843 |> assign(:user, admin)
2844 |> post("/api/pleroma/admin/relay", %{
2845 relay_url: "http://mastodon.example.org/users/admin"
2850 |> assign(:user, admin)
2851 |> delete("/api/pleroma/admin/relay", %{
2852 relay_url: "http://mastodon.example.org/users/admin"
2855 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
2857 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
2859 assert ModerationLog.get_log_entry_message(log_entry_one) ==
2860 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
2862 assert ModerationLog.get_log_entry_message(log_entry_two) ==
2863 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
2867 describe "instances" do
2868 test "GET /instances/:instance/statuses" do
2869 admin = insert(:user, is_admin: true)
2870 user = insert(:user, local: false, nickname: "archaeme@archae.me")
2871 user2 = insert(:user, local: false, nickname: "test@test.com")
2872 insert_pair(:note_activity, user: user)
2873 insert(:note_activity, user: user2)
2877 |> assign(:user, admin)
2878 |> get("/api/pleroma/admin/instances/archae.me/statuses")
2880 response = json_response(conn, 200)
2882 assert length(response) == 2
2886 |> assign(:user, admin)
2887 |> get("/api/pleroma/admin/instances/test.com/statuses")
2889 response = json_response(conn, 200)
2891 assert length(response) == 1
2895 |> assign(:user, admin)
2896 |> get("/api/pleroma/admin/instances/nonexistent.com/statuses")
2898 response = json_response(conn, 200)
2900 assert length(response) == 0
2904 describe "PATCH /confirm_email" do
2905 setup %{conn: conn} do
2906 admin = insert(:user, is_admin: true)
2908 %{conn: assign(conn, :user, admin), admin: admin}
2911 test "it confirms emails of two users", %{admin: admin} do
2912 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2914 assert first_user.confirmation_pending == true
2915 assert second_user.confirmation_pending == true
2918 |> assign(:user, admin)
2919 |> patch("/api/pleroma/admin/users/confirm_email", %{
2921 first_user.nickname,
2922 second_user.nickname
2926 assert first_user.confirmation_pending == true
2927 assert second_user.confirmation_pending == true
2929 log_entry = Repo.one(ModerationLog)
2931 assert ModerationLog.get_log_entry_message(log_entry) ==
2932 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
2933 second_user.nickname
2938 describe "PATCH /resend_confirmation_email" do
2939 setup %{conn: conn} do
2940 admin = insert(:user, is_admin: true)
2942 %{conn: assign(conn, :user, admin), admin: admin}
2945 test "it resend emails for two users", %{admin: admin} do
2946 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
2949 |> assign(:user, admin)
2950 |> patch("/api/pleroma/admin/users/resend_confirmation_email", %{
2952 first_user.nickname,
2953 second_user.nickname
2957 log_entry = Repo.one(ModerationLog)
2959 assert ModerationLog.get_log_entry_message(log_entry) ==
2960 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
2961 second_user.nickname
2967 # Needed for testing
2968 defmodule Pleroma.Web.Endpoint.NotReal do
2971 defmodule Pleroma.Captcha.NotReal do