First
[anni] / test / pleroma / web / admin_api / controllers / report_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.ReportControllerTest do
6   use Pleroma.Web.ConnCase, async: false
7
8   import Pleroma.Factory
9
10   alias Pleroma.Activity
11   alias Pleroma.ModerationLog
12   alias Pleroma.Repo
13   alias Pleroma.ReportNote
14   alias Pleroma.Web.CommonAPI
15
16   setup do
17     admin = insert(:user, is_admin: true)
18     token = insert(:oauth_admin_token, user: admin)
19
20     conn =
21       build_conn()
22       |> assign(:user, admin)
23       |> assign(:token, token)
24
25     {:ok, %{admin: admin, token: token, conn: conn}}
26   end
27
28   describe "GET /api/pleroma/admin/reports/:id" do
29     setup do
30       clear_config([:instance, :admin_privileges], [:reports_manage_reports])
31     end
32
33     test "returns 403 if not privileged with :reports_manage_reports", %{conn: conn} do
34       clear_config([:instance, :admin_privileges], [])
35
36       conn =
37         conn
38         |> get("/api/pleroma/admin/reports/report_id")
39
40       assert json_response(conn, :forbidden)
41     end
42
43     test "returns report by its id", %{conn: conn} do
44       [reporter, target_user] = insert_pair(:user)
45       activity = insert(:note_activity, user: target_user)
46
47       {:ok, %{id: report_id}} =
48         CommonAPI.report(reporter, %{
49           account_id: target_user.id,
50           comment: "I feel offended",
51           status_ids: [activity.id]
52         })
53
54       conn
55       |> put_req_header("content-type", "application/json")
56       |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
57         content: "this is an admin note"
58       })
59
60       response =
61         conn
62         |> get("/api/pleroma/admin/reports/#{report_id}")
63         |> json_response_and_validate_schema(:ok)
64
65       assert response["id"] == report_id
66
67       [notes] = response["notes"]
68       assert notes["content"] == "this is an admin note"
69     end
70
71     test "renders reported content even if the status is deleted", %{conn: conn} do
72       [reporter, target_user] = insert_pair(:user)
73       activity = insert(:note_activity, user: target_user)
74       activity = Activity.normalize(activity)
75
76       {:ok, %{id: report_id}} =
77         CommonAPI.report(reporter, %{
78           account_id: target_user.id,
79           comment: "I feel offended",
80           status_ids: [activity.id]
81         })
82
83       CommonAPI.delete(activity.id, target_user)
84
85       response =
86         conn
87         |> get("/api/pleroma/admin/reports/#{report_id}")
88         |> json_response_and_validate_schema(:ok)
89
90       assert response["id"] == report_id
91
92       assert [status] = response["statuses"]
93       assert activity.object.data["id"] == status["uri"]
94       assert activity.object.data["content"] == status["content"]
95     end
96
97     test "returns 404 when report id is invalid", %{conn: conn} do
98       conn = get(conn, "/api/pleroma/admin/reports/test")
99
100       assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
101     end
102   end
103
104   describe "PATCH /api/pleroma/admin/reports" do
105     setup do
106       clear_config([:instance, :admin_privileges], [:reports_manage_reports])
107
108       [reporter, target_user] = insert_pair(:user)
109       activity = insert(:note_activity, user: target_user)
110
111       {:ok, %{id: report_id}} =
112         CommonAPI.report(reporter, %{
113           account_id: target_user.id,
114           comment: "I feel offended",
115           status_ids: [activity.id]
116         })
117
118       {:ok, %{id: second_report_id}} =
119         CommonAPI.report(reporter, %{
120           account_id: target_user.id,
121           comment: "I feel very offended",
122           status_ids: [activity.id]
123         })
124
125       %{
126         id: report_id,
127         second_report_id: second_report_id
128       }
129     end
130
131     test "returns 403 if not privileged with :reports_manage_reports", %{
132       conn: conn,
133       id: id,
134       admin: admin
135     } do
136       clear_config([:instance, :admin_privileges], [])
137
138       conn =
139         conn
140         |> assign(:token, insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]))
141         |> put_req_header("content-type", "application/json")
142         |> patch("/api/pleroma/admin/reports", %{
143           "reports" => [%{"state" => "resolved", "id" => id}]
144         })
145
146       assert json_response(conn, :forbidden)
147     end
148
149     test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
150       read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
151       write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
152
153       response =
154         conn
155         |> assign(:token, read_token)
156         |> put_req_header("content-type", "application/json")
157         |> patch("/api/pleroma/admin/reports", %{
158           "reports" => [%{"state" => "resolved", "id" => id}]
159         })
160         |> json_response_and_validate_schema(403)
161
162       assert response == %{
163                "error" => "Insufficient permissions: admin:write:reports."
164              }
165
166       conn
167       |> assign(:token, write_token)
168       |> put_req_header("content-type", "application/json")
169       |> patch("/api/pleroma/admin/reports", %{
170         "reports" => [%{"state" => "resolved", "id" => id}]
171       })
172       |> json_response_and_validate_schema(:no_content)
173     end
174
175     test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
176       conn
177       |> put_req_header("content-type", "application/json")
178       |> patch("/api/pleroma/admin/reports", %{
179         "reports" => [
180           %{"state" => "resolved", "id" => id}
181         ]
182       })
183       |> json_response_and_validate_schema(:no_content)
184
185       activity = Activity.get_by_id_with_user_actor(id)
186       assert activity.data["state"] == "resolved"
187
188       log_entry = Repo.one(ModerationLog)
189
190       assert ModerationLog.get_log_entry_message(log_entry) ==
191                "@#{admin.nickname} updated report ##{id} (on user @#{activity.user_actor.nickname}) with 'resolved' state"
192     end
193
194     test "closes report", %{conn: conn, id: id, admin: admin} do
195       conn
196       |> put_req_header("content-type", "application/json")
197       |> patch("/api/pleroma/admin/reports", %{
198         "reports" => [
199           %{"state" => "closed", "id" => id}
200         ]
201       })
202       |> json_response_and_validate_schema(:no_content)
203
204       activity = Activity.get_by_id_with_user_actor(id)
205       assert activity.data["state"] == "closed"
206
207       log_entry = Repo.one(ModerationLog)
208
209       assert ModerationLog.get_log_entry_message(log_entry) ==
210                "@#{admin.nickname} updated report ##{id} (on user @#{activity.user_actor.nickname}) with 'closed' state"
211     end
212
213     test "returns 400 when state is unknown", %{conn: conn, id: id} do
214       conn =
215         conn
216         |> put_req_header("content-type", "application/json")
217         |> patch("/api/pleroma/admin/reports", %{
218           "reports" => [
219             %{"state" => "test", "id" => id}
220           ]
221         })
222
223       assert "Unsupported state" =
224                hd(json_response_and_validate_schema(conn, :bad_request))["error"]
225     end
226
227     test "returns 404 when report is not exist", %{conn: conn} do
228       conn =
229         conn
230         |> put_req_header("content-type", "application/json")
231         |> patch("/api/pleroma/admin/reports", %{
232           "reports" => [
233             %{"state" => "closed", "id" => "test"}
234           ]
235         })
236
237       assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found"
238     end
239
240     test "updates state of multiple reports", %{
241       conn: conn,
242       id: id,
243       admin: admin,
244       second_report_id: second_report_id
245     } do
246       conn
247       |> put_req_header("content-type", "application/json")
248       |> patch("/api/pleroma/admin/reports", %{
249         "reports" => [
250           %{"state" => "resolved", "id" => id},
251           %{"state" => "closed", "id" => second_report_id}
252         ]
253       })
254       |> json_response_and_validate_schema(:no_content)
255
256       activity = Activity.get_by_id_with_user_actor(id)
257       second_activity = Activity.get_by_id_with_user_actor(second_report_id)
258       assert activity.data["state"] == "resolved"
259       assert second_activity.data["state"] == "closed"
260
261       [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
262
263       assert ModerationLog.get_log_entry_message(first_log_entry) ==
264                "@#{admin.nickname} updated report ##{id} (on user @#{activity.user_actor.nickname}) with 'resolved' state"
265
266       assert ModerationLog.get_log_entry_message(second_log_entry) ==
267                "@#{admin.nickname} updated report ##{second_report_id} (on user @#{second_activity.user_actor.nickname}) with 'closed' state"
268     end
269   end
270
271   describe "GET /api/pleroma/admin/reports" do
272     setup do
273       clear_config([:instance, :admin_privileges], [:reports_manage_reports])
274     end
275
276     test "returns 403 if not privileged with :reports_manage_reports", %{conn: conn} do
277       clear_config([:instance, :admin_privileges], [])
278
279       conn =
280         conn
281         |> get(report_path(conn, :index))
282
283       assert json_response(conn, :forbidden)
284     end
285
286     test "returns empty response when no reports created", %{conn: conn} do
287       response =
288         conn
289         |> get(report_path(conn, :index))
290         |> json_response_and_validate_schema(:ok)
291
292       assert Enum.empty?(response["reports"])
293       assert response["total"] == 0
294     end
295
296     test "returns reports", %{conn: conn} do
297       [reporter, target_user] = insert_pair(:user)
298       activity = insert(:note_activity, user: target_user)
299
300       {:ok, %{id: report_id}} =
301         CommonAPI.report(reporter, %{
302           account_id: target_user.id,
303           comment: "I feel offended",
304           status_ids: [activity.id]
305         })
306
307       response =
308         conn
309         |> get(report_path(conn, :index))
310         |> json_response_and_validate_schema(:ok)
311
312       [report] = response["reports"]
313
314       assert length(response["reports"]) == 1
315       assert report["id"] == report_id
316
317       assert response["total"] == 1
318     end
319
320     test "returns reports with specified state", %{conn: conn} do
321       [reporter, target_user] = insert_pair(:user)
322       activity = insert(:note_activity, user: target_user)
323
324       {:ok, %{id: first_report_id}} =
325         CommonAPI.report(reporter, %{
326           account_id: target_user.id,
327           comment: "I feel offended",
328           status_ids: [activity.id]
329         })
330
331       {:ok, %{id: second_report_id}} =
332         CommonAPI.report(reporter, %{
333           account_id: target_user.id,
334           comment: "I don't like this user"
335         })
336
337       CommonAPI.update_report_state(second_report_id, "closed")
338
339       response =
340         conn
341         |> get(report_path(conn, :index, %{state: "open"}))
342         |> json_response_and_validate_schema(:ok)
343
344       assert [open_report] = response["reports"]
345
346       assert length(response["reports"]) == 1
347       assert open_report["id"] == first_report_id
348
349       assert response["total"] == 1
350
351       response =
352         conn
353         |> get(report_path(conn, :index, %{state: "closed"}))
354         |> json_response_and_validate_schema(:ok)
355
356       assert [closed_report] = response["reports"]
357
358       assert length(response["reports"]) == 1
359       assert closed_report["id"] == second_report_id
360
361       assert response["total"] == 1
362
363       assert %{"total" => 0, "reports" => []} ==
364                conn
365                |> get(report_path(conn, :index, %{state: "resolved"}))
366                |> json_response_and_validate_schema(:ok)
367     end
368
369     test "renders content correctly", %{conn: conn} do
370       [reporter, target_user] = insert_pair(:user)
371       note = insert(:note, user: target_user, data: %{"content" => "mew 1"})
372       note2 = insert(:note, user: target_user, data: %{"content" => "mew 2"})
373       activity = insert(:note_activity, user: target_user, note: note)
374       activity2 = insert(:note_activity, user: target_user, note: note2)
375
376       {:ok, _report} =
377         CommonAPI.report(reporter, %{
378           account_id: target_user.id,
379           comment: "I feel offended",
380           status_ids: [activity.id, activity2.id]
381         })
382
383       CommonAPI.delete(activity.id, target_user)
384       CommonAPI.delete(activity2.id, target_user)
385
386       response =
387         conn
388         |> get(report_path(conn, :index))
389         |> json_response_and_validate_schema(:ok)
390
391       assert [open_report] = response["reports"]
392       assert %{"statuses" => [s1, s2]} = open_report
393       assert "mew 1" in [s1["content"], s2["content"]]
394       assert "mew 2" in [s1["content"], s2["content"]]
395     end
396
397     test "returns 403 when requested by a non-admin" do
398       user = insert(:user)
399       token = insert(:oauth_token, user: user)
400
401       conn =
402         build_conn()
403         |> assign(:user, user)
404         |> assign(:token, token)
405         |> get("/api/pleroma/admin/reports")
406
407       assert json_response(conn, :forbidden) ==
408                %{"error" => "User is not a staff member."}
409     end
410
411     test "returns 403 when requested by anonymous" do
412       conn = get(build_conn(), "/api/pleroma/admin/reports")
413
414       assert json_response(conn, :forbidden) == %{
415                "error" => "Invalid credentials."
416              }
417     end
418   end
419
420   describe "POST /api/pleroma/admin/reports/:id/notes" do
421     setup %{conn: conn, admin: admin} do
422       clear_config([:instance, :admin_privileges], [:reports_manage_reports])
423
424       [reporter, target_user] = insert_pair(:user)
425       activity = insert(:note_activity, user: target_user)
426
427       {:ok, %{id: report_id}} =
428         CommonAPI.report(reporter, %{
429           account_id: target_user.id,
430           comment: "I feel offended",
431           status_ids: [activity.id]
432         })
433
434       conn
435       |> put_req_header("content-type", "application/json")
436       |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
437         content: "this is disgusting!"
438       })
439
440       conn
441       |> put_req_header("content-type", "application/json")
442       |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
443         content: "this is disgusting2!"
444       })
445
446       %{
447         admin_id: admin.id,
448         report_id: report_id
449       }
450     end
451
452     test "returns 403 if not privileged with :reports_manage_reports", %{
453       conn: conn,
454       report_id: report_id
455     } do
456       clear_config([:instance, :admin_privileges], [])
457
458       post_conn =
459         conn
460         |> put_req_header("content-type", "application/json")
461         |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
462           content: "this is disgusting2!"
463         })
464
465       delete_conn = delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/note.id")
466
467       assert json_response(post_conn, :forbidden)
468       assert json_response(delete_conn, :forbidden)
469     end
470
471     test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
472       assert [note, _] = Repo.all(ReportNote)
473
474       assert %{
475                activity_id: ^report_id,
476                content: "this is disgusting!",
477                user_id: ^admin_id
478              } = note
479     end
480
481     test "it returns reports with notes", %{conn: conn, admin: admin} do
482       conn = get(conn, "/api/pleroma/admin/reports")
483
484       response = json_response_and_validate_schema(conn, 200)
485       notes = hd(response["reports"])["notes"]
486       [note, _] = notes
487
488       assert note["user"]["nickname"] == admin.nickname
489       # We use '=~' because the order of the notes isn't guaranteed
490       assert note["content"] =~ "this is disgusting"
491       assert note["created_at"]
492       assert response["total"] == 1
493     end
494
495     test "it deletes the note", %{conn: conn, report_id: report_id} do
496       assert ReportNote |> Repo.all() |> length() == 2
497       assert [note, _] = Repo.all(ReportNote)
498
499       delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
500
501       assert ReportNote |> Repo.all() |> length() == 1
502     end
503   end
504 end