move to 2.5.5
[anni] / test / pleroma / web / admin_api / controllers / admin_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
6   use Pleroma.Web.ConnCase, async: false
7   use Oban.Testing, repo: Pleroma.Repo
8
9   import ExUnit.CaptureLog
10   import Pleroma.Factory
11   import Swoosh.TestAssertions
12
13   alias Pleroma.Activity
14   alias Pleroma.MFA
15   alias Pleroma.ModerationLog
16   alias Pleroma.Repo
17   alias Pleroma.Tests.ObanHelpers
18   alias Pleroma.User
19   alias Pleroma.Web.CommonAPI
20
21   setup_all do
22     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
23
24     :ok
25   end
26
27   setup do
28     admin = insert(:user, is_admin: true)
29     token = insert(:oauth_admin_token, user: admin)
30
31     conn =
32       build_conn()
33       |> assign(:user, admin)
34       |> assign(:token, token)
35
36     {:ok, %{admin: admin, token: token, conn: conn}}
37   end
38
39   test "with valid `admin_token` query parameter, skips OAuth scopes check" do
40     clear_config([:admin_token], "password123")
41
42     user = insert(:user)
43
44     conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123")
45
46     assert json_response(conn, 200)
47   end
48
49   test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
50        %{admin: admin} do
51     user = insert(:user)
52     url = "/api/pleroma/admin/users/#{user.nickname}"
53
54     good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
55     good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
56     good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
57
58     bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
59     bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
60     bad_token3 = nil
61
62     for good_token <- [good_token1, good_token2, good_token3] do
63       conn =
64         build_conn()
65         |> assign(:user, admin)
66         |> assign(:token, good_token)
67         |> get(url)
68
69       assert json_response(conn, 200)
70     end
71
72     for good_token <- [good_token1, good_token2, good_token3] do
73       conn =
74         build_conn()
75         |> assign(:user, nil)
76         |> assign(:token, good_token)
77         |> get(url)
78
79       assert json_response(conn, :forbidden)
80     end
81
82     for bad_token <- [bad_token1, bad_token2, bad_token3] do
83       conn =
84         build_conn()
85         |> assign(:user, admin)
86         |> assign(:token, bad_token)
87         |> get(url)
88
89       assert json_response(conn, :forbidden)
90     end
91   end
92
93   describe "PUT /api/pleroma/admin/users/tag" do
94     setup %{conn: conn} do
95       clear_config([:instance, :admin_privileges], [:users_manage_tags])
96
97       user1 = insert(:user, %{tags: ["x"]})
98       user2 = insert(:user, %{tags: ["y"]})
99       user3 = insert(:user, %{tags: ["unchanged"]})
100
101       %{conn: conn, user1: user1, user2: user2, user3: user3}
102     end
103
104     test "it appends specified tags to users with specified nicknames", %{
105       conn: conn,
106       admin: admin,
107       user1: user1,
108       user2: user2
109     } do
110       conn =
111         conn
112         |> put_req_header("accept", "application/json")
113         |> put(
114           "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
115             "#{user2.nickname}&tags[]=foo&tags[]=bar"
116         )
117
118       assert empty_json_response(conn)
119       assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
120       assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
121
122       log_entry = Repo.one(ModerationLog)
123
124       users =
125         [user1.nickname, user2.nickname]
126         |> Enum.map(&"@#{&1}")
127         |> Enum.join(", ")
128
129       tags = ["foo", "bar"] |> Enum.join(", ")
130
131       assert ModerationLog.get_log_entry_message(log_entry) ==
132                "@#{admin.nickname} added tags: #{tags} to users: #{users}"
133     end
134
135     test "it does not modify tags of not specified users", %{
136       conn: conn,
137       user1: user1,
138       user2: user2,
139       user3: user3
140     } do
141       conn =
142         conn
143         |> put_req_header("accept", "application/json")
144         |> put(
145           "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
146             "#{user2.nickname}&tags[]=foo&tags[]=bar"
147         )
148
149       assert empty_json_response(conn)
150       assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
151     end
152
153     test "it requires privileged role :users_manage_tags", %{conn: conn} do
154       clear_config([:instance, :admin_privileges], [])
155
156       response =
157         conn
158         |> put_req_header("accept", "application/json")
159         |> put("/api/pleroma/admin/users/tag?nicknames[]=nickname&tags[]=foo&tags[]=bar")
160
161       assert json_response(response, :forbidden)
162     end
163   end
164
165   describe "DELETE /api/pleroma/admin/users/tag" do
166     setup %{conn: conn} do
167       clear_config([:instance, :admin_privileges], [:users_manage_tags])
168       user1 = insert(:user, %{tags: ["x"]})
169       user2 = insert(:user, %{tags: ["y", "z"]})
170       user3 = insert(:user, %{tags: ["unchanged"]})
171
172       %{conn: conn, user1: user1, user2: user2, user3: user3}
173     end
174
175     test "it removes specified tags from users with specified nicknames", %{
176       conn: conn,
177       admin: admin,
178       user1: user1,
179       user2: user2
180     } do
181       conn =
182         conn
183         |> put_req_header("accept", "application/json")
184         |> delete(
185           "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
186             "#{user2.nickname}&tags[]=x&tags[]=z"
187         )
188
189       assert empty_json_response(conn)
190       assert User.get_cached_by_id(user1.id).tags == []
191       assert User.get_cached_by_id(user2.id).tags == ["y"]
192
193       log_entry = Repo.one(ModerationLog)
194
195       users =
196         [user1.nickname, user2.nickname]
197         |> Enum.map(&"@#{&1}")
198         |> Enum.join(", ")
199
200       tags = ["x", "z"] |> Enum.join(", ")
201
202       assert ModerationLog.get_log_entry_message(log_entry) ==
203                "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
204     end
205
206     test "it does not modify tags of not specified users", %{
207       conn: conn,
208       user1: user1,
209       user2: user2,
210       user3: user3
211     } do
212       conn =
213         conn
214         |> put_req_header("accept", "application/json")
215         |> delete(
216           "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
217             "#{user2.nickname}&tags[]=x&tags[]=z"
218         )
219
220       assert empty_json_response(conn)
221       assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
222     end
223
224     test "it requires privileged role :users_manage_tags", %{conn: conn} do
225       clear_config([:instance, :admin_privileges], [])
226
227       response =
228         conn
229         |> put_req_header("accept", "application/json")
230         |> delete("/api/pleroma/admin/users/tag?nicknames[]=nickname&tags[]=foo&tags[]=bar")
231
232       assert json_response(response, :forbidden)
233     end
234   end
235
236   describe "/api/pleroma/admin/users/:nickname/permission_group" do
237     test "GET is giving user_info", %{admin: admin, conn: conn} do
238       conn =
239         conn
240         |> put_req_header("accept", "application/json")
241         |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
242
243       assert json_response(conn, 200) == %{
244                "is_admin" => true,
245                "is_moderator" => false
246              }
247     end
248
249     test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
250       user = insert(:user)
251
252       conn =
253         conn
254         |> put_req_header("accept", "application/json")
255         |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
256
257       assert json_response(conn, 200) == %{
258                "is_admin" => true
259              }
260
261       log_entry = Repo.one(ModerationLog)
262
263       assert ModerationLog.get_log_entry_message(log_entry) ==
264                "@#{admin.nickname} made @#{user.nickname} admin"
265     end
266
267     test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
268       user_one = insert(:user)
269       user_two = insert(:user)
270
271       conn =
272         conn
273         |> put_req_header("accept", "application/json")
274         |> post("/api/pleroma/admin/users/permission_group/admin", %{
275           nicknames: [user_one.nickname, user_two.nickname]
276         })
277
278       assert json_response(conn, 200) == %{"is_admin" => true}
279
280       log_entry = Repo.one(ModerationLog)
281
282       assert ModerationLog.get_log_entry_message(log_entry) ==
283                "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
284     end
285
286     test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
287       user = insert(:user, is_admin: true)
288
289       conn =
290         conn
291         |> put_req_header("accept", "application/json")
292         |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
293
294       assert json_response(conn, 200) == %{"is_admin" => false}
295
296       log_entry = Repo.one(ModerationLog)
297
298       assert ModerationLog.get_log_entry_message(log_entry) ==
299                "@#{admin.nickname} revoked admin role from @#{user.nickname}"
300     end
301
302     test "/:right DELETE, can remove from a permission group (multiple)", %{
303       admin: admin,
304       conn: conn
305     } do
306       user_one = insert(:user, is_admin: true)
307       user_two = insert(:user, is_admin: true)
308
309       conn =
310         conn
311         |> put_req_header("accept", "application/json")
312         |> delete("/api/pleroma/admin/users/permission_group/admin", %{
313           nicknames: [user_one.nickname, user_two.nickname]
314         })
315
316       assert json_response(conn, 200) == %{"is_admin" => false}
317
318       log_entry = Repo.one(ModerationLog)
319
320       assert ModerationLog.get_log_entry_message(log_entry) ==
321                "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{user_two.nickname}"
322     end
323   end
324
325   describe "/api/pleroma/admin/users/:nickname/password_reset" do
326     test "it returns a password reset link", %{conn: conn} do
327       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
328
329       user = insert(:user)
330
331       conn =
332         conn
333         |> put_req_header("accept", "application/json")
334         |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
335
336       resp = json_response(conn, 200)
337
338       assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
339     end
340
341     test "it requires privileged role :users_manage_credentials", %{conn: conn} do
342       clear_config([:instance, :admin_privileges], [])
343
344       response =
345         conn
346         |> put_req_header("accept", "application/json")
347         |> get("/api/pleroma/admin/users/nickname/password_reset")
348
349       assert json_response(response, :forbidden)
350     end
351   end
352
353   describe "PUT disable_mfa" do
354     test "returns 200 and disable 2fa", %{conn: conn} do
355       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
356
357       user =
358         insert(:user,
359           multi_factor_authentication_settings: %MFA.Settings{
360             enabled: true,
361             totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
362           }
363         )
364
365       response =
366         conn
367         |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
368         |> json_response(200)
369
370       assert response == user.nickname
371       mfa_settings = refresh_record(user).multi_factor_authentication_settings
372
373       refute mfa_settings.enabled
374       refute mfa_settings.totp.confirmed
375     end
376
377     test "returns 404 if user not found", %{conn: conn} do
378       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
379
380       response =
381         conn
382         |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
383         |> json_response(404)
384
385       assert response == %{"error" => "Not found"}
386     end
387
388     test "it requires privileged role :users_manage_credentials", %{conn: conn} do
389       clear_config([:instance, :admin_privileges], [])
390
391       response =
392         conn
393         |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
394
395       assert json_response(response, :forbidden)
396     end
397   end
398
399   describe "GET /api/pleroma/admin/restart" do
400     setup do: clear_config(:configurable_from_database, true)
401
402     test "pleroma restarts", %{conn: conn} do
403       capture_log(fn ->
404         assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
405       end) =~ "pleroma restarted"
406
407       refute Restarter.Pleroma.need_reboot?()
408     end
409   end
410
411   test "need_reboot flag", %{conn: conn} do
412     assert conn
413            |> get("/api/pleroma/admin/need_reboot")
414            |> json_response(200) == %{"need_reboot" => false}
415
416     Restarter.Pleroma.need_reboot()
417
418     assert conn
419            |> get("/api/pleroma/admin/need_reboot")
420            |> json_response(200) == %{"need_reboot" => true}
421
422     on_exit(fn -> Restarter.Pleroma.refresh() end)
423   end
424
425   describe "GET /api/pleroma/admin/users/:nickname/statuses" do
426     setup do
427       clear_config([:instance, :admin_privileges], [:messages_read])
428
429       user = insert(:user)
430
431       insert(:note_activity, user: user)
432       insert(:note_activity, user: user)
433       insert(:note_activity, user: user)
434
435       %{user: user}
436     end
437
438     test "renders user's statuses", %{conn: conn, user: user} do
439       conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
440
441       assert %{"total" => 3, "activities" => activities} = json_response(conn, 200)
442       assert length(activities) == 3
443     end
444
445     test "it requires privileged role :messages_read", %{conn: conn, user: user} do
446       clear_config([:instance, :admin_privileges], [])
447
448       conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
449
450       assert json_response(conn, :forbidden)
451     end
452
453     test "renders user's statuses with pagination", %{conn: conn, user: user} do
454       %{"total" => 3, "activities" => [activity1]} =
455         conn
456         |> get("/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=1")
457         |> json_response(200)
458
459       %{"total" => 3, "activities" => [activity2]} =
460         conn
461         |> get("/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=2")
462         |> json_response(200)
463
464       refute activity1 == activity2
465     end
466
467     test "doesn't return private statuses by default", %{conn: conn, user: user} do
468       {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
469
470       {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
471
472       %{"total" => 4, "activities" => activities} =
473         conn
474         |> get("/api/pleroma/admin/users/#{user.nickname}/statuses")
475         |> json_response(200)
476
477       assert length(activities) == 4
478     end
479
480     test "returns private statuses with godmode on", %{conn: conn, user: user} do
481       {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"})
482
483       {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
484
485       %{"total" => 5, "activities" => activities} =
486         conn
487         |> get("/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
488         |> json_response(200)
489
490       assert length(activities) == 5
491     end
492
493     test "excludes reblogs by default", %{conn: conn, user: user} do
494       other_user = insert(:user)
495       {:ok, activity} = CommonAPI.post(user, %{status: "."})
496       {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
497
498       assert %{"total" => 0, "activities" => []} ==
499                conn
500                |> get("/api/pleroma/admin/users/#{other_user.nickname}/statuses")
501                |> json_response(200)
502
503       assert %{"total" => 1, "activities" => [_]} =
504                conn
505                |> get(
506                  "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true"
507                )
508                |> json_response(200)
509     end
510   end
511
512   describe "GET /api/pleroma/admin/users/:nickname/chats" do
513     setup do
514       clear_config([:instance, :admin_privileges], [:messages_read])
515
516       user = insert(:user)
517
518       %{user: user}
519     end
520
521     test "renders user's chats", %{conn: conn, user: user} do
522       recipients = insert_list(3, :user)
523
524       Enum.each(recipients, fn recipient ->
525         CommonAPI.post_chat_message(user, recipient, "yo")
526       end)
527
528       conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
529
530       assert json_response(conn, 200) |> length() == 3
531     end
532
533     test "it requires privileged role :messages_read", %{conn: conn, user: user} do
534       clear_config([:instance, :admin_privileges], [])
535
536       conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
537
538       assert json_response(conn, :forbidden)
539     end
540   end
541
542   describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do
543     setup do
544       user = insert(:user)
545       recipient = insert(:user)
546       CommonAPI.post_chat_message(user, recipient, "yo")
547       %{conn: conn} = oauth_access(["read:chats"])
548       %{conn: conn, user: user}
549     end
550
551     test "returns 403", %{conn: conn, user: user} do
552       conn
553       |> get("/api/pleroma/admin/users/#{user.nickname}/chats")
554       |> json_response(403)
555     end
556   end
557
558   describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do
559     setup do
560       user = insert(:user)
561       recipient = insert(:user)
562       CommonAPI.post_chat_message(user, recipient, "yo")
563       %{conn: build_conn(), user: user}
564     end
565
566     test "returns 403", %{conn: conn, user: user} do
567       conn
568       |> get("/api/pleroma/admin/users/#{user.nickname}/chats")
569       |> json_response(403)
570     end
571   end
572
573   describe "GET /api/pleroma/admin/moderation_log" do
574     setup do
575       clear_config([:instance, :admin_privileges], [:moderation_log_read])
576       moderator = insert(:user, is_moderator: true)
577
578       %{moderator: moderator}
579     end
580
581     test "returns the log", %{conn: conn, admin: admin} do
582       Repo.insert(%ModerationLog{
583         data: %{
584           actor: %{
585             "id" => admin.id,
586             "nickname" => admin.nickname,
587             "type" => "user"
588           },
589           action: "relay_follow",
590           target: "https://example.org/relay"
591         },
592         inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
593       })
594
595       Repo.insert(%ModerationLog{
596         data: %{
597           actor: %{
598             "id" => admin.id,
599             "nickname" => admin.nickname,
600             "type" => "user"
601           },
602           action: "relay_unfollow",
603           target: "https://example.org/relay"
604         },
605         inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
606       })
607
608       conn = get(conn, "/api/pleroma/admin/moderation_log")
609
610       response = json_response(conn, 200)
611       [first_entry, second_entry] = response["items"]
612
613       assert response["total"] == 2
614       assert first_entry["data"]["action"] == "relay_unfollow"
615
616       assert first_entry["message"] ==
617                "@#{admin.nickname} unfollowed relay: https://example.org/relay"
618
619       assert second_entry["data"]["action"] == "relay_follow"
620
621       assert second_entry["message"] ==
622                "@#{admin.nickname} followed relay: https://example.org/relay"
623     end
624
625     test "returns the log with pagination", %{conn: conn, admin: admin} do
626       Repo.insert(%ModerationLog{
627         data: %{
628           actor: %{
629             "id" => admin.id,
630             "nickname" => admin.nickname,
631             "type" => "user"
632           },
633           action: "relay_follow",
634           target: "https://example.org/relay"
635         },
636         inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
637       })
638
639       Repo.insert(%ModerationLog{
640         data: %{
641           actor: %{
642             "id" => admin.id,
643             "nickname" => admin.nickname,
644             "type" => "user"
645           },
646           action: "relay_unfollow",
647           target: "https://example.org/relay"
648         },
649         inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
650       })
651
652       conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
653
654       response1 = json_response(conn1, 200)
655       [first_entry] = response1["items"]
656
657       assert response1["total"] == 2
658       assert response1["items"] |> length() == 1
659       assert first_entry["data"]["action"] == "relay_unfollow"
660
661       assert first_entry["message"] ==
662                "@#{admin.nickname} unfollowed relay: https://example.org/relay"
663
664       conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
665
666       response2 = json_response(conn2, 200)
667       [second_entry] = response2["items"]
668
669       assert response2["total"] == 2
670       assert response2["items"] |> length() == 1
671       assert second_entry["data"]["action"] == "relay_follow"
672
673       assert second_entry["message"] ==
674                "@#{admin.nickname} followed relay: https://example.org/relay"
675     end
676
677     test "filters log by date", %{conn: conn, admin: admin} do
678       first_date = "2017-08-15T15:47:06Z"
679       second_date = "2017-08-20T15:47:06Z"
680
681       Repo.insert(%ModerationLog{
682         data: %{
683           actor: %{
684             "id" => admin.id,
685             "nickname" => admin.nickname,
686             "type" => "user"
687           },
688           action: "relay_follow",
689           target: "https://example.org/relay"
690         },
691         inserted_at: NaiveDateTime.from_iso8601!(first_date)
692       })
693
694       Repo.insert(%ModerationLog{
695         data: %{
696           actor: %{
697             "id" => admin.id,
698             "nickname" => admin.nickname,
699             "type" => "user"
700           },
701           action: "relay_unfollow",
702           target: "https://example.org/relay"
703         },
704         inserted_at: NaiveDateTime.from_iso8601!(second_date)
705       })
706
707       conn1 =
708         get(
709           conn,
710           "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
711         )
712
713       response1 = json_response(conn1, 200)
714       [first_entry] = response1["items"]
715
716       assert response1["total"] == 1
717       assert first_entry["data"]["action"] == "relay_unfollow"
718
719       assert first_entry["message"] ==
720                "@#{admin.nickname} unfollowed relay: https://example.org/relay"
721     end
722
723     test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
724       Repo.insert(%ModerationLog{
725         data: %{
726           actor: %{
727             "id" => admin.id,
728             "nickname" => admin.nickname,
729             "type" => "user"
730           },
731           action: "relay_follow",
732           target: "https://example.org/relay"
733         }
734       })
735
736       Repo.insert(%ModerationLog{
737         data: %{
738           actor: %{
739             "id" => moderator.id,
740             "nickname" => moderator.nickname,
741             "type" => "user"
742           },
743           action: "relay_unfollow",
744           target: "https://example.org/relay"
745         }
746       })
747
748       conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
749
750       response1 = json_response(conn1, 200)
751       [first_entry] = response1["items"]
752
753       assert response1["total"] == 1
754       assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
755     end
756
757     test "returns log filtered by search", %{conn: conn, moderator: moderator} do
758       ModerationLog.insert_log(%{
759         actor: moderator,
760         action: "relay_follow",
761         target: "https://example.org/relay"
762       })
763
764       ModerationLog.insert_log(%{
765         actor: moderator,
766         action: "relay_unfollow",
767         target: "https://example.org/relay"
768       })
769
770       conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
771
772       response1 = json_response(conn1, 200)
773       [first_entry] = response1["items"]
774
775       assert response1["total"] == 1
776
777       assert get_in(first_entry, ["data", "message"]) ==
778                "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
779     end
780
781     test "it requires privileged role :moderation_log_read", %{conn: conn} do
782       clear_config([:instance, :admin_privileges], [])
783
784       assert conn
785              |> put_req_header("content-type", "multipart/form-data")
786              |> get("/api/pleroma/admin/moderation_log")
787              |> json_response(:forbidden)
788     end
789   end
790
791   test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated",
792        %{conn: conn} do
793     clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated)
794     user = insert(:user, %{local: false, nickname: "u@peer1.com"})
795     conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
796
797     assert json_response(conn, 200)
798   end
799
800   describe "GET /users/:nickname/credentials" do
801     test "gets the user credentials", %{conn: conn} do
802       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
803       user = insert(:user)
804       conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
805
806       response = assert json_response(conn, 200)
807       assert response["email"] == user.email
808     end
809
810     test "returns 403 if requested by a non-admin" do
811       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
812       user = insert(:user)
813
814       conn =
815         build_conn()
816         |> assign(:user, user)
817         |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
818
819       assert json_response(conn, :forbidden)
820     end
821
822     test "it requires privileged role :users_manage_credentials", %{conn: conn} do
823       clear_config([:instance, :admin_privileges], [])
824
825       response =
826         conn
827         |> get("/api/pleroma/admin/users/nickname/credentials")
828
829       assert json_response(response, :forbidden)
830     end
831   end
832
833   describe "PATCH /users/:nickname/credentials" do
834     setup do
835       user = insert(:user)
836       [user: user]
837     end
838
839     test "changes password and email", %{conn: conn, admin: admin, user: user} do
840       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
841
842       assert user.password_reset_pending == false
843
844       conn =
845         patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
846           "password" => "new_password",
847           "email" => "new_email@example.com",
848           "name" => "new_name"
849         })
850
851       assert json_response(conn, 200) == %{"status" => "success"}
852
853       ObanHelpers.perform_all()
854
855       updated_user = User.get_by_id(user.id)
856
857       assert updated_user.email == "new_email@example.com"
858       assert updated_user.name == "new_name"
859       assert updated_user.password_hash != user.password_hash
860       assert updated_user.password_reset_pending == true
861
862       [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
863
864       assert ModerationLog.get_log_entry_message(log_entry1) ==
865                "@#{admin.nickname} updated users: @#{user.nickname}"
866
867       assert ModerationLog.get_log_entry_message(log_entry2) ==
868                "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
869     end
870
871     test "returns 403 if requested by a non-admin", %{user: user} do
872       conn =
873         build_conn()
874         |> assign(:user, user)
875         |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
876           "password" => "new_password",
877           "email" => "new_email@example.com",
878           "name" => "new_name"
879         })
880
881       assert json_response(conn, :forbidden)
882     end
883
884     test "returns 403 if not privileged with :users_manage_credentials", %{conn: conn, user: user} do
885       clear_config([:instance, :admin_privileges], [])
886
887       conn =
888         patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
889           "password" => "new_password",
890           "email" => "new_email@example.com",
891           "name" => "new_name"
892         })
893
894       assert json_response(conn, :forbidden)
895     end
896
897     test "changes actor type from permitted list", %{conn: conn, user: user} do
898       assert user.actor_type == "Person"
899
900       assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
901                "actor_type" => "Service"
902              })
903              |> json_response(200) == %{"status" => "success"}
904
905       updated_user = User.get_by_id(user.id)
906
907       assert updated_user.actor_type == "Service"
908
909       assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
910                "actor_type" => "Application"
911              })
912              |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}
913     end
914
915     test "update non existing user", %{conn: conn} do
916       assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
917                "password" => "new_password"
918              })
919              |> json_response(404) == %{"error" => "Not found"}
920     end
921   end
922
923   describe "PATCH /users/:nickname/force_password_reset" do
924     test "sets password_reset_pending to true", %{conn: conn} do
925       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
926       user = insert(:user)
927       assert user.password_reset_pending == false
928
929       conn =
930         patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
931
932       assert empty_json_response(conn) == ""
933
934       ObanHelpers.perform_all()
935
936       assert User.get_by_id(user.id).password_reset_pending == true
937     end
938
939     test "it requires privileged role :users_manage_credentials", %{conn: conn} do
940       clear_config([:instance, :admin_privileges], [])
941
942       response =
943         conn
944         |> patch("/api/pleroma/admin/users/force_password_reset", %{nickname: "nickname"})
945
946       assert json_response(response, :forbidden)
947     end
948   end
949
950   describe "PATCH /confirm_email" do
951     test "it confirms emails of two users", %{conn: conn, admin: admin} do
952       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
953       [first_user, second_user] = insert_pair(:user, is_confirmed: false)
954
955       refute first_user.is_confirmed
956       refute second_user.is_confirmed
957
958       ret_conn =
959         patch(conn, "/api/pleroma/admin/users/confirm_email", %{
960           nicknames: [
961             first_user.nickname,
962             second_user.nickname
963           ]
964         })
965
966       assert ret_conn.status == 200
967
968       first_user = User.get_by_id(first_user.id)
969       second_user = User.get_by_id(second_user.id)
970
971       assert first_user.is_confirmed
972       assert second_user.is_confirmed
973
974       log_entry = Repo.one(ModerationLog)
975
976       assert ModerationLog.get_log_entry_message(log_entry) ==
977                "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{second_user.nickname}"
978     end
979
980     test "it requires privileged role :users_manage_credentials", %{conn: conn} do
981       clear_config([:instance, :admin_privileges], [])
982
983       response =
984         conn
985         |> patch("/api/pleroma/admin/users/confirm_email", %{nicknames: ["nickname"]})
986
987       assert json_response(response, :forbidden)
988     end
989   end
990
991   describe "PATCH /resend_confirmation_email" do
992     test "it resend emails for two users", %{conn: conn, admin: admin} do
993       clear_config([:instance, :admin_privileges], [:users_manage_credentials])
994       [first_user, second_user] = insert_pair(:user, is_confirmed: false)
995
996       ret_conn =
997         patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
998           nicknames: [
999             first_user.nickname,
1000             second_user.nickname
1001           ]
1002         })
1003
1004       assert ret_conn.status == 200
1005
1006       log_entry = Repo.one(ModerationLog)
1007
1008       assert ModerationLog.get_log_entry_message(log_entry) ==
1009                "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{second_user.nickname}"
1010
1011       ObanHelpers.perform_all()
1012
1013       Pleroma.Emails.UserEmail.account_confirmation_email(first_user)
1014       # temporary hackney fix until hackney max_connections bug is fixed
1015       # https://git.pleroma.social/pleroma/pleroma/-/issues/2101
1016       |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
1017       |> assert_email_sent()
1018     end
1019
1020     test "it requires privileged role :users_manage_credentials", %{conn: conn} do
1021       clear_config([:instance, :admin_privileges], [])
1022
1023       response =
1024         conn
1025         |> patch("/api/pleroma/admin/users/resend_confirmation_email", %{nicknames: ["nickname"]})
1026
1027       assert json_response(response, :forbidden)
1028     end
1029   end
1030
1031   describe "/api/pleroma/admin/stats" do
1032     setup do
1033       clear_config([:instance, :admin_privileges], [:statistics_read])
1034     end
1035
1036     test "status visibility count", %{conn: conn} do
1037       user = insert(:user)
1038       CommonAPI.post(user, %{visibility: "public", status: "hey"})
1039       CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1040       CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
1041
1042       response =
1043         conn
1044         |> get("/api/pleroma/admin/stats")
1045         |> json_response(200)
1046
1047       assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
1048                response["status_visibility"]
1049     end
1050
1051     test "by instance", %{conn: conn} do
1052       user1 = insert(:user)
1053       instance2 = "instance2.tld"
1054       user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
1055
1056       CommonAPI.post(user1, %{visibility: "public", status: "hey"})
1057       CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"})
1058       CommonAPI.post(user2, %{visibility: "private", status: "hey"})
1059
1060       response =
1061         conn
1062         |> get("/api/pleroma/admin/stats", instance: instance2)
1063         |> json_response(200)
1064
1065       assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
1066                response["status_visibility"]
1067     end
1068
1069     test "it requires privileged role :statistics_read", %{conn: conn} do
1070       clear_config([:instance, :admin_privileges], [])
1071
1072       assert conn
1073              |> get("/api/pleroma/admin/stats", instance: "lain.wired")
1074              |> json_response(:forbidden)
1075     end
1076   end
1077
1078   describe "/api/pleroma/backups" do
1079     test "it creates a backup", %{conn: conn} do
1080       admin = %{id: admin_id, nickname: admin_nickname} = insert(:user, is_admin: true)
1081       token = insert(:oauth_admin_token, user: admin)
1082       user = %{id: user_id, nickname: user_nickname} = insert(:user)
1083
1084       assert "" ==
1085                conn
1086                |> assign(:user, admin)
1087                |> assign(:token, token)
1088                |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
1089                |> json_response(200)
1090
1091       assert [backup] = Repo.all(Pleroma.User.Backup)
1092
1093       ObanHelpers.perform_all()
1094
1095       email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup, admin.id)
1096
1097       assert String.contains?(email.html_body, "Admin @#{admin.nickname} requested a full backup")
1098       assert_email_sent(to: {user.name, user.email}, html_body: email.html_body)
1099
1100       log_message = "@#{admin_nickname} requested account backup for @#{user_nickname}"
1101
1102       assert [
1103                %{
1104                  data: %{
1105                    "action" => "create_backup",
1106                    "actor" => %{
1107                      "id" => ^admin_id,
1108                      "nickname" => ^admin_nickname
1109                    },
1110                    "message" => ^log_message,
1111                    "subject" => %{
1112                      "id" => ^user_id,
1113                      "nickname" => ^user_nickname
1114                    }
1115                  }
1116                }
1117              ] = Pleroma.ModerationLog |> Repo.all()
1118     end
1119
1120     test "it doesn't limit admins", %{conn: conn} do
1121       admin = insert(:user, is_admin: true)
1122       token = insert(:oauth_admin_token, user: admin)
1123       user = insert(:user)
1124
1125       assert "" ==
1126                conn
1127                |> assign(:user, admin)
1128                |> assign(:token, token)
1129                |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
1130                |> json_response(200)
1131
1132       assert [_backup] = Repo.all(Pleroma.User.Backup)
1133
1134       assert "" ==
1135                conn
1136                |> assign(:user, admin)
1137                |> assign(:token, token)
1138                |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
1139                |> json_response(200)
1140
1141       assert Repo.aggregate(Pleroma.User.Backup, :count) == 2
1142     end
1143   end
1144
1145   describe "POST /api/v1/pleroma/admin/reload_emoji" do
1146     setup do
1147       clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
1148
1149       admin = insert(:user, is_admin: true)
1150       token = insert(:oauth_admin_token, user: admin)
1151
1152       conn =
1153         build_conn()
1154         |> assign(:user, admin)
1155         |> assign(:token, token)
1156
1157       {:ok, %{conn: conn, admin: admin}}
1158     end
1159
1160     test "it requires privileged role :emoji_manage_emoji", %{conn: conn} do
1161       assert conn
1162              |> post("/api/v1/pleroma/admin/reload_emoji")
1163              |> json_response(200)
1164
1165       clear_config([:instance, :admin_privileges], [])
1166
1167       assert conn
1168              |> post("/api/v1/pleroma/admin/reload_emoji")
1169              |> json_response(:forbidden)
1170     end
1171   end
1172 end
1173
1174 # Needed for testing
1175 defmodule Pleroma.Web.Endpoint.NotReal do
1176 end
1177
1178 defmodule Pleroma.Captcha.NotReal do
1179 end