Add OpenAPI spec for AdminAPI.ReportController See merge request pleroma/pleroma!26281570-levenshtein-distance-user-search
@@ -547,7 +547,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret | |||||
```json | ```json | ||||
{ | { | ||||
"totalReports" : 1, | |||||
"total" : 1, | |||||
"reports": [ | "reports": [ | ||||
{ | { | ||||
"account": { | "account": { | ||||
@@ -768,7 +768,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret | |||||
- 400 Bad Request `"Invalid parameters"` when `status` is missing | - 400 Bad Request `"Invalid parameters"` when `status` is missing | ||||
- On success: `204`, empty response | - On success: `204`, empty response | ||||
## `POST /api/pleroma/admin/reports/:report_id/notes/:id` | |||||
## `DELETE /api/pleroma/admin/reports/:report_id/notes/:id` | |||||
### Delete report note | ### Delete report note | ||||
@@ -740,6 +740,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do | |||||
def get_reports(params, page, page_size) do | def get_reports(params, page, page_size) do | ||||
params = | params = | ||||
params | params | ||||
|> Map.new(fn {key, value} -> {to_string(key), value} end) | |||||
|> Map.put("type", "Flag") | |> Map.put("type", "Flag") | ||||
|> Map.put("skip_preload", true) | |> Map.put("skip_preload", true) | ||||
|> Map.put("preload_report_notes", true) | |> Map.put("preload_report_notes", true) | ||||
@@ -7,28 +7,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3] | import Pleroma.Web.ControllerHelper, only: [json_response: 3] | ||||
alias Pleroma.Activity | |||||
alias Pleroma.Config | alias Pleroma.Config | ||||
alias Pleroma.ConfigDB | alias Pleroma.ConfigDB | ||||
alias Pleroma.MFA | alias Pleroma.MFA | ||||
alias Pleroma.ModerationLog | alias Pleroma.ModerationLog | ||||
alias Pleroma.Plugs.OAuthScopesPlug | alias Pleroma.Plugs.OAuthScopesPlug | ||||
alias Pleroma.ReportNote | |||||
alias Pleroma.Stats | alias Pleroma.Stats | ||||
alias Pleroma.User | alias Pleroma.User | ||||
alias Pleroma.Web.ActivityPub.ActivityPub | alias Pleroma.Web.ActivityPub.ActivityPub | ||||
alias Pleroma.Web.ActivityPub.Builder | alias Pleroma.Web.ActivityPub.Builder | ||||
alias Pleroma.Web.ActivityPub.Pipeline | alias Pleroma.Web.ActivityPub.Pipeline | ||||
alias Pleroma.Web.ActivityPub.Relay | alias Pleroma.Web.ActivityPub.Relay | ||||
alias Pleroma.Web.ActivityPub.Utils | |||||
alias Pleroma.Web.AdminAPI | alias Pleroma.Web.AdminAPI | ||||
alias Pleroma.Web.AdminAPI.AccountView | alias Pleroma.Web.AdminAPI.AccountView | ||||
alias Pleroma.Web.AdminAPI.ConfigView | alias Pleroma.Web.AdminAPI.ConfigView | ||||
alias Pleroma.Web.AdminAPI.ModerationLogView | alias Pleroma.Web.AdminAPI.ModerationLogView | ||||
alias Pleroma.Web.AdminAPI.Report | |||||
alias Pleroma.Web.AdminAPI.ReportView | |||||
alias Pleroma.Web.AdminAPI.Search | alias Pleroma.Web.AdminAPI.Search | ||||
alias Pleroma.Web.CommonAPI | |||||
alias Pleroma.Web.Endpoint | alias Pleroma.Web.Endpoint | ||||
alias Pleroma.Web.Router | alias Pleroma.Web.Router | ||||
@@ -73,18 +67,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||||
plug( | plug( | ||||
OAuthScopesPlug, | OAuthScopesPlug, | ||||
%{scopes: ["read:reports"], admin: true} | |||||
when action in [:list_reports, :report_show] | |||||
) | |||||
plug( | |||||
OAuthScopesPlug, | |||||
%{scopes: ["write:reports"], admin: true} | |||||
when action in [:reports_update, :report_notes_create, :report_notes_delete] | |||||
) | |||||
plug( | |||||
OAuthScopesPlug, | |||||
%{scopes: ["read:statuses"], admin: true} | %{scopes: ["read:statuses"], admin: true} | ||||
when action in [:list_user_statuses, :list_instance_statuses] | when action in [:list_user_statuses, :list_instance_statuses] | ||||
) | ) | ||||
@@ -645,85 +627,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||||
end | end | ||||
end | end | ||||
def list_reports(conn, params) do | |||||
{page, page_size} = page_params(params) | |||||
reports = Utils.get_reports(params, page, page_size) | |||||
conn | |||||
|> put_view(ReportView) | |||||
|> render("index.json", %{reports: reports}) | |||||
end | |||||
def report_show(conn, %{"id" => id}) do | |||||
with %Activity{} = report <- Activity.get_by_id(id) do | |||||
conn | |||||
|> put_view(ReportView) | |||||
|> render("show.json", Report.extract_report_info(report)) | |||||
else | |||||
_ -> {:error, :not_found} | |||||
end | |||||
end | |||||
def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do | |||||
result = | |||||
reports | |||||
|> Enum.map(fn report -> | |||||
with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do | |||||
ModerationLog.insert_log(%{ | |||||
action: "report_update", | |||||
actor: admin, | |||||
subject: activity | |||||
}) | |||||
activity | |||||
else | |||||
{:error, message} -> %{id: report["id"], error: message} | |||||
end | |||||
end) | |||||
case Enum.any?(result, &Map.has_key?(&1, :error)) do | |||||
true -> json_response(conn, :bad_request, result) | |||||
false -> json_response(conn, :no_content, "") | |||||
end | |||||
end | |||||
def report_notes_create(%{assigns: %{user: user}} = conn, %{ | |||||
"id" => report_id, | |||||
"content" => content | |||||
}) do | |||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do | |||||
ModerationLog.insert_log(%{ | |||||
action: "report_note", | |||||
actor: user, | |||||
subject: Activity.get_by_id(report_id), | |||||
text: content | |||||
}) | |||||
json_response(conn, :no_content, "") | |||||
else | |||||
_ -> json_response(conn, :bad_request, "") | |||||
end | |||||
end | |||||
def report_notes_delete(%{assigns: %{user: user}} = conn, %{ | |||||
"id" => note_id, | |||||
"report_id" => report_id | |||||
}) do | |||||
with {:ok, note} <- ReportNote.destroy(note_id) do | |||||
ModerationLog.insert_log(%{ | |||||
action: "report_note_delete", | |||||
actor: user, | |||||
subject: Activity.get_by_id(report_id), | |||||
text: note.content | |||||
}) | |||||
json_response(conn, :no_content, "") | |||||
else | |||||
_ -> json_response(conn, :bad_request, "") | |||||
end | |||||
end | |||||
def list_log(conn, params) do | def list_log(conn, params) do | ||||
{page, page_size} = page_params(params) | {page, page_size} = page_params(params) | ||||
@@ -0,0 +1,107 @@ | |||||
# Pleroma: A lightweight social networking server | |||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||||
# SPDX-License-Identifier: AGPL-3.0-only | |||||
defmodule Pleroma.Web.AdminAPI.ReportController do | |||||
use Pleroma.Web, :controller | |||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3] | |||||
alias Pleroma.Activity | |||||
alias Pleroma.ModerationLog | |||||
alias Pleroma.Plugs.OAuthScopesPlug | |||||
alias Pleroma.ReportNote | |||||
alias Pleroma.Web.ActivityPub.Utils | |||||
alias Pleroma.Web.AdminAPI | |||||
alias Pleroma.Web.AdminAPI.Report | |||||
alias Pleroma.Web.CommonAPI | |||||
require Logger | |||||
plug(Pleroma.Web.ApiSpec.CastAndValidate) | |||||
plug(OAuthScopesPlug, %{scopes: ["read:reports"], admin: true} when action in [:index, :show]) | |||||
plug( | |||||
OAuthScopesPlug, | |||||
%{scopes: ["write:reports"], admin: true} | |||||
when action in [:update, :notes_create, :notes_delete] | |||||
) | |||||
action_fallback(AdminAPI.FallbackController) | |||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation | |||||
def index(conn, params) do | |||||
reports = Utils.get_reports(params, params.page, params.page_size) | |||||
render(conn, "index.json", reports: reports) | |||||
end | |||||
def show(conn, %{id: id}) do | |||||
with %Activity{} = report <- Activity.get_by_id(id) do | |||||
render(conn, "show.json", Report.extract_report_info(report)) | |||||
else | |||||
_ -> {:error, :not_found} | |||||
end | |||||
end | |||||
def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do | |||||
result = | |||||
Enum.map(reports, fn report -> | |||||
case CommonAPI.update_report_state(report.id, report.state) do | |||||
{:ok, activity} -> | |||||
ModerationLog.insert_log(%{ | |||||
action: "report_update", | |||||
actor: admin, | |||||
subject: activity | |||||
}) | |||||
activity | |||||
{:error, message} -> | |||||
%{id: report.id, error: message} | |||||
end | |||||
end) | |||||
if Enum.any?(result, &Map.has_key?(&1, :error)) do | |||||
json_response(conn, :bad_request, result) | |||||
else | |||||
json_response(conn, :no_content, "") | |||||
end | |||||
end | |||||
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{ | |||||
id: report_id | |||||
}) do | |||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do | |||||
ModerationLog.insert_log(%{ | |||||
action: "report_note", | |||||
actor: user, | |||||
subject: Activity.get_by_id(report_id), | |||||
text: content | |||||
}) | |||||
json_response(conn, :no_content, "") | |||||
else | |||||
_ -> json_response(conn, :bad_request, "") | |||||
end | |||||
end | |||||
def notes_delete(%{assigns: %{user: user}} = conn, %{ | |||||
id: note_id, | |||||
report_id: report_id | |||||
}) do | |||||
with {:ok, note} <- ReportNote.destroy(note_id) do | |||||
ModerationLog.insert_log(%{ | |||||
action: "report_note_delete", | |||||
actor: user, | |||||
subject: Activity.get_by_id(report_id), | |||||
text: note.content | |||||
}) | |||||
json_response(conn, :no_content, "") | |||||
else | |||||
_ -> json_response(conn, :bad_request, "") | |||||
end | |||||
end | |||||
end |
@@ -0,0 +1,237 @@ | |||||
# Pleroma: A lightweight social networking server | |||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||||
# SPDX-License-Identifier: AGPL-3.0-only | |||||
defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do | |||||
alias OpenApiSpex.Operation | |||||
alias OpenApiSpex.Schema | |||||
alias Pleroma.Web.ApiSpec.Schemas.Account | |||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError | |||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID | |||||
alias Pleroma.Web.ApiSpec.Schemas.Status | |||||
import Pleroma.Web.ApiSpec.Helpers | |||||
def open_api_operation(action) do | |||||
operation = String.to_existing_atom("#{action}_operation") | |||||
apply(__MODULE__, operation, []) | |||||
end | |||||
def index_operation do | |||||
%Operation{ | |||||
tags: ["Admin", "Reports"], | |||||
summary: "Get a list of reports", | |||||
operationId: "AdminAPI.ReportController.index", | |||||
security: [%{"oAuth" => ["read:reports"]}], | |||||
parameters: [ | |||||
Operation.parameter( | |||||
:state, | |||||
:query, | |||||
report_state(), | |||||
"Filter by report state" | |||||
), | |||||
Operation.parameter( | |||||
:limit, | |||||
:query, | |||||
%Schema{type: :integer}, | |||||
"The number of records to retrieve" | |||||
), | |||||
Operation.parameter( | |||||
:page, | |||||
:query, | |||||
%Schema{type: :integer, default: 1}, | |||||
"Page number" | |||||
), | |||||
Operation.parameter( | |||||
:page_size, | |||||
:query, | |||||
%Schema{type: :integer, default: 50}, | |||||
"Number number of log entries per page" | |||||
) | |||||
], | |||||
responses: %{ | |||||
200 => | |||||
Operation.response("Response", "application/json", %Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
total: %Schema{type: :integer}, | |||||
reports: %Schema{ | |||||
type: :array, | |||||
items: report() | |||||
} | |||||
} | |||||
}), | |||||
403 => Operation.response("Forbidden", "application/json", ApiError) | |||||
} | |||||
} | |||||
end | |||||
def show_operation do | |||||
%Operation{ | |||||
tags: ["Admin", "Reports"], | |||||
summary: "Get an individual report", | |||||
operationId: "AdminAPI.ReportController.show", | |||||
parameters: [id_param()], | |||||
security: [%{"oAuth" => ["read:reports"]}], | |||||
responses: %{ | |||||
200 => Operation.response("Report", "application/json", report()), | |||||
404 => Operation.response("Not Found", "application/json", ApiError) | |||||
} | |||||
} | |||||
end | |||||
def update_operation do | |||||
%Operation{ | |||||
tags: ["Admin", "Reports"], | |||||
summary: "Change the state of one or multiple reports", | |||||
operationId: "AdminAPI.ReportController.update", | |||||
security: [%{"oAuth" => ["write:reports"]}], | |||||
requestBody: request_body("Parameters", update_request(), required: true), | |||||
responses: %{ | |||||
204 => no_content_response(), | |||||
400 => Operation.response("Bad Request", "application/json", update_400_response()), | |||||
403 => Operation.response("Forbidden", "application/json", ApiError) | |||||
} | |||||
} | |||||
end | |||||
def notes_create_operation do | |||||
%Operation{ | |||||
tags: ["Admin", "Reports"], | |||||
summary: "Create report note", | |||||
operationId: "AdminAPI.ReportController.notes_create", | |||||
parameters: [id_param()], | |||||
requestBody: | |||||
request_body("Parameters", %Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
content: %Schema{type: :string, description: "The message"} | |||||
} | |||||
}), | |||||
security: [%{"oAuth" => ["write:reports"]}], | |||||
responses: %{ | |||||
204 => no_content_response(), | |||||
404 => Operation.response("Not Found", "application/json", ApiError) | |||||
} | |||||
} | |||||
end | |||||
def notes_delete_operation do | |||||
%Operation{ | |||||
tags: ["Admin", "Reports"], | |||||
summary: "Delete report note", | |||||
operationId: "AdminAPI.ReportController.notes_delete", | |||||
parameters: [ | |||||
Operation.parameter(:report_id, :path, :string, "Report ID"), | |||||
Operation.parameter(:id, :path, :string, "Note ID") | |||||
], | |||||
security: [%{"oAuth" => ["write:reports"]}], | |||||
responses: %{ | |||||
204 => no_content_response(), | |||||
404 => Operation.response("Not Found", "application/json", ApiError) | |||||
} | |||||
} | |||||
end | |||||
defp report_state do | |||||
%Schema{type: :string, enum: ["open", "closed", "resolved"]} | |||||
end | |||||
defp id_param do | |||||
Operation.parameter(:id, :path, FlakeID, "Report ID", | |||||
example: "9umDrYheeY451cQnEe", | |||||
required: true | |||||
) | |||||
end | |||||
defp report do | |||||
%Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
id: FlakeID, | |||||
state: report_state(), | |||||
account: account_admin(), | |||||
actor: account_admin(), | |||||
content: %Schema{type: :string}, | |||||
created_at: %Schema{type: :string, format: :"date-time"}, | |||||
statuses: %Schema{type: :array, items: Status}, | |||||
notes: %Schema{ | |||||
type: :array, | |||||
items: %Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
id: %Schema{type: :integer}, | |||||
user_id: FlakeID, | |||||
content: %Schema{type: :string}, | |||||
inserted_at: %Schema{type: :string, format: :"date-time"} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
end | |||||
defp account_admin do | |||||
%Schema{ | |||||
title: "Account", | |||||
description: "Account view for admins", | |||||
type: :object, | |||||
properties: | |||||
Map.merge(Account.schema().properties, %{ | |||||
nickname: %Schema{type: :string}, | |||||
deactivated: %Schema{type: :boolean}, | |||||
local: %Schema{type: :boolean}, | |||||
roles: %Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
admin: %Schema{type: :boolean}, | |||||
moderator: %Schema{type: :boolean} | |||||
} | |||||
}, | |||||
confirmation_pending: %Schema{type: :boolean} | |||||
}) | |||||
} | |||||
end | |||||
defp update_request do | |||||
%Schema{ | |||||
type: :object, | |||||
required: [:reports], | |||||
properties: %{ | |||||
reports: %Schema{ | |||||
type: :array, | |||||
items: %Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
id: %Schema{allOf: [FlakeID], description: "Required, report ID"}, | |||||
state: %Schema{ | |||||
type: :string, | |||||
description: | |||||
"Required, the new state. Valid values are `open`, `closed` and `resolved`" | |||||
} | |||||
} | |||||
}, | |||||
example: %{ | |||||
"reports" => [ | |||||
%{"id" => "123", "state" => "closed"}, | |||||
%{"id" => "1337", "state" => "resolved"} | |||||
] | |||||
} | |||||
} | |||||
} | |||||
} | |||||
end | |||||
defp update_400_response do | |||||
%Schema{ | |||||
type: :array, | |||||
items: %Schema{ | |||||
type: :object, | |||||
properties: %{ | |||||
id: %Schema{allOf: [FlakeID], description: "Report ID"}, | |||||
error: %Schema{type: :string, description: "Error message"} | |||||
} | |||||
} | |||||
} | |||||
end | |||||
end |
@@ -123,7 +123,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do | |||||
} | } | ||||
end | end | ||||
defp admin_account do | |||||
def admin_account do | |||||
%Schema{ | %Schema{ | ||||
type: :object, | type: :object, | ||||
properties: %{ | properties: %{ | ||||
@@ -183,11 +183,11 @@ defmodule Pleroma.Web.Router do | |||||
patch("/users/confirm_email", AdminAPIController, :confirm_email) | patch("/users/confirm_email", AdminAPIController, :confirm_email) | ||||
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email) | patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email) | ||||
get("/reports", AdminAPIController, :list_reports) | |||||
get("/reports/:id", AdminAPIController, :report_show) | |||||
patch("/reports", AdminAPIController, :reports_update) | |||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create) | |||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete) | |||||
get("/reports", ReportController, :index) | |||||
get("/reports/:id", ReportController, :show) | |||||
patch("/reports", ReportController, :update) | |||||
post("/reports/:id/notes", ReportController, :notes_create) | |||||
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete) | |||||
get("/statuses/:id", StatusController, :show) | get("/statuses/:id", StatusController, :show) | ||||
put("/statuses/:id", StatusController, :update) | put("/statuses/:id", StatusController, :update) | ||||
@@ -17,7 +17,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||||
alias Pleroma.MFA | alias Pleroma.MFA | ||||
alias Pleroma.ModerationLog | alias Pleroma.ModerationLog | ||||
alias Pleroma.Repo | alias Pleroma.Repo | ||||
alias Pleroma.ReportNote | |||||
alias Pleroma.Tests.ObanHelpers | alias Pleroma.Tests.ObanHelpers | ||||
alias Pleroma.User | alias Pleroma.User | ||||
alias Pleroma.Web | alias Pleroma.Web | ||||
@@ -1198,286 +1197,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||||
end | end | ||||
end | end | ||||
describe "GET /api/pleroma/admin/reports/:id" do | |||||
test "returns report by its id", %{conn: conn} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports/#{report_id}") | |||||
|> json_response(:ok) | |||||
assert response["id"] == report_id | |||||
end | |||||
test "returns 404 when report id is invalid", %{conn: conn} do | |||||
conn = get(conn, "/api/pleroma/admin/reports/test") | |||||
assert json_response(conn, :not_found) == %{"error" => "Not found"} | |||||
end | |||||
end | |||||
describe "PATCH /api/pleroma/admin/reports" do | |||||
setup do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
{:ok, %{id: second_report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel very offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
%{ | |||||
id: report_id, | |||||
second_report_id: second_report_id | |||||
} | |||||
end | |||||
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do | |||||
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) | |||||
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) | |||||
response = | |||||
conn | |||||
|> assign(:token, read_token) | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [%{"state" => "resolved", "id" => id}] | |||||
}) | |||||
|> json_response(403) | |||||
assert response == %{ | |||||
"error" => "Insufficient permissions: admin:write:reports." | |||||
} | |||||
conn | |||||
|> assign(:token, write_token) | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [%{"state" => "resolved", "id" => id}] | |||||
}) | |||||
|> json_response(:no_content) | |||||
end | |||||
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do | |||||
conn | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "resolved", "id" => id} | |||||
] | |||||
}) | |||||
|> json_response(:no_content) | |||||
activity = Activity.get_by_id(id) | |||||
assert activity.data["state"] == "resolved" | |||||
log_entry = Repo.one(ModerationLog) | |||||
assert ModerationLog.get_log_entry_message(log_entry) == | |||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state" | |||||
end | |||||
test "closes report", %{conn: conn, id: id, admin: admin} do | |||||
conn | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "closed", "id" => id} | |||||
] | |||||
}) | |||||
|> json_response(:no_content) | |||||
activity = Activity.get_by_id(id) | |||||
assert activity.data["state"] == "closed" | |||||
log_entry = Repo.one(ModerationLog) | |||||
assert ModerationLog.get_log_entry_message(log_entry) == | |||||
"@#{admin.nickname} updated report ##{id} with 'closed' state" | |||||
end | |||||
test "returns 400 when state is unknown", %{conn: conn, id: id} do | |||||
conn = | |||||
conn | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "test", "id" => id} | |||||
] | |||||
}) | |||||
assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state" | |||||
end | |||||
test "returns 404 when report is not exist", %{conn: conn} do | |||||
conn = | |||||
conn | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "closed", "id" => "test"} | |||||
] | |||||
}) | |||||
assert hd(json_response(conn, :bad_request))["error"] == "not_found" | |||||
end | |||||
test "updates state of multiple reports", %{ | |||||
conn: conn, | |||||
id: id, | |||||
admin: admin, | |||||
second_report_id: second_report_id | |||||
} do | |||||
conn | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "resolved", "id" => id}, | |||||
%{"state" => "closed", "id" => second_report_id} | |||||
] | |||||
}) | |||||
|> json_response(:no_content) | |||||
activity = Activity.get_by_id(id) | |||||
second_activity = Activity.get_by_id(second_report_id) | |||||
assert activity.data["state"] == "resolved" | |||||
assert second_activity.data["state"] == "closed" | |||||
[first_log_entry, second_log_entry] = Repo.all(ModerationLog) | |||||
assert ModerationLog.get_log_entry_message(first_log_entry) == | |||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state" | |||||
assert ModerationLog.get_log_entry_message(second_log_entry) == | |||||
"@#{admin.nickname} updated report ##{second_report_id} with 'closed' state" | |||||
end | |||||
end | |||||
describe "GET /api/pleroma/admin/reports" do | |||||
test "returns empty response when no reports created", %{conn: conn} do | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports") | |||||
|> json_response(:ok) | |||||
assert Enum.empty?(response["reports"]) | |||||
assert response["total"] == 0 | |||||
end | |||||
test "returns reports", %{conn: conn} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports") | |||||
|> json_response(:ok) | |||||
[report] = response["reports"] | |||||
assert length(response["reports"]) == 1 | |||||
assert report["id"] == report_id | |||||
assert response["total"] == 1 | |||||
end | |||||
test "returns reports with specified state", %{conn: conn} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: first_report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
{:ok, %{id: second_report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I don't like this user" | |||||
}) | |||||
CommonAPI.update_report_state(second_report_id, "closed") | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports", %{ | |||||
"state" => "open" | |||||
}) | |||||
|> json_response(:ok) | |||||
[open_report] = response["reports"] | |||||
assert length(response["reports"]) == 1 | |||||
assert open_report["id"] == first_report_id | |||||
assert response["total"] == 1 | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports", %{ | |||||
"state" => "closed" | |||||
}) | |||||
|> json_response(:ok) | |||||
[closed_report] = response["reports"] | |||||
assert length(response["reports"]) == 1 | |||||
assert closed_report["id"] == second_report_id | |||||
assert response["total"] == 1 | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports", %{ | |||||
"state" => "resolved" | |||||
}) | |||||
|> json_response(:ok) | |||||
assert Enum.empty?(response["reports"]) | |||||
assert response["total"] == 0 | |||||
end | |||||
test "returns 403 when requested by a non-admin" do | |||||
user = insert(:user) | |||||
token = insert(:oauth_token, user: user) | |||||
conn = | |||||
build_conn() | |||||
|> assign(:user, user) | |||||
|> assign(:token, token) | |||||
|> get("/api/pleroma/admin/reports") | |||||
assert json_response(conn, :forbidden) == | |||||
%{"error" => "User is not an admin or OAuth admin scope is not granted."} | |||||
end | |||||
test "returns 403 when requested by anonymous" do | |||||
conn = get(build_conn(), "/api/pleroma/admin/reports") | |||||
assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."} | |||||
end | |||||
end | |||||
describe "GET /api/pleroma/admin/config" do | describe "GET /api/pleroma/admin/config" do | ||||
setup do: clear_config(:configurable_from_database, true) | setup do: clear_config(:configurable_from_database, true) | ||||
@@ -3195,66 +2914,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||||
end | end | ||||
end | end | ||||
describe "POST /reports/:id/notes" do | |||||
setup %{conn: conn, admin: admin} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ | |||||
content: "this is disgusting!" | |||||
}) | |||||
post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{ | |||||
content: "this is disgusting2!" | |||||
}) | |||||
%{ | |||||
admin_id: admin.id, | |||||
report_id: report_id | |||||
} | |||||
end | |||||
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do | |||||
[note, _] = Repo.all(ReportNote) | |||||
assert %{ | |||||
activity_id: ^report_id, | |||||
content: "this is disgusting!", | |||||
user_id: ^admin_id | |||||
} = note | |||||
end | |||||
test "it returns reports with notes", %{conn: conn, admin: admin} do | |||||
conn = get(conn, "/api/pleroma/admin/reports") | |||||
response = json_response(conn, 200) | |||||
notes = hd(response["reports"])["notes"] | |||||
[note, _] = notes | |||||
assert note["user"]["nickname"] == admin.nickname | |||||
assert note["content"] == "this is disgusting!" | |||||
assert note["created_at"] | |||||
assert response["total"] == 1 | |||||
end | |||||
test "it deletes the note", %{conn: conn, report_id: report_id} do | |||||
assert ReportNote |> Repo.all() |> length() == 2 | |||||
[note, _] = Repo.all(ReportNote) | |||||
delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") | |||||
assert ReportNote |> Repo.all() |> length() == 1 | |||||
end | |||||
end | |||||
describe "GET /api/pleroma/admin/config/descriptions" do | describe "GET /api/pleroma/admin/config/descriptions" do | ||||
test "structure", %{conn: conn} do | test "structure", %{conn: conn} do | ||||
admin = insert(:user, is_admin: true) | admin = insert(:user, is_admin: true) | ||||
@@ -0,0 +1,374 @@ | |||||
# Pleroma: A lightweight social networking server | |||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||||
# SPDX-License-Identifier: AGPL-3.0-only | |||||
defmodule Pleroma.Web.AdminAPI.ReportControllerTest do | |||||
use Pleroma.Web.ConnCase | |||||
import Pleroma.Factory | |||||
alias Pleroma.Activity | |||||
alias Pleroma.Config | |||||
alias Pleroma.ModerationLog | |||||
alias Pleroma.Repo | |||||
alias Pleroma.ReportNote | |||||
alias Pleroma.Web.CommonAPI | |||||
setup do | |||||
admin = insert(:user, is_admin: true) | |||||
token = insert(:oauth_admin_token, user: admin) | |||||
conn = | |||||
build_conn() | |||||
|> assign(:user, admin) | |||||
|> assign(:token, token) | |||||
{:ok, %{admin: admin, token: token, conn: conn}} | |||||
end | |||||
describe "GET /api/pleroma/admin/reports/:id" do | |||||
test "returns report by its id", %{conn: conn} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports/#{report_id}") | |||||
|> json_response_and_validate_schema(:ok) | |||||
assert response["id"] == report_id | |||||
end | |||||
test "returns 404 when report id is invalid", %{conn: conn} do | |||||
conn = get(conn, "/api/pleroma/admin/reports/test") | |||||
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} | |||||
end | |||||
end | |||||
describe "PATCH /api/pleroma/admin/reports" do | |||||
setup do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
{:ok, %{id: second_report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel very offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
%{ | |||||
id: report_id, | |||||
second_report_id: second_report_id | |||||
} | |||||
end | |||||
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do | |||||
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) | |||||
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) | |||||
response = | |||||
conn | |||||
|> assign(:token, read_token) | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [%{"state" => "resolved", "id" => id}] | |||||
}) | |||||
|> json_response_and_validate_schema(403) | |||||
assert response == %{ | |||||
"error" => "Insufficient permissions: admin:write:reports." | |||||
} | |||||
conn | |||||
|> assign(:token, write_token) | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [%{"state" => "resolved", "id" => id}] | |||||
}) | |||||
|> json_response_and_validate_schema(:no_content) | |||||
end | |||||
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "resolved", "id" => id} | |||||
] | |||||
}) | |||||
|> json_response_and_validate_schema(:no_content) | |||||
activity = Activity.get_by_id(id) | |||||
assert activity.data["state"] == "resolved" | |||||
log_entry = Repo.one(ModerationLog) | |||||
assert ModerationLog.get_log_entry_message(log_entry) == | |||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state" | |||||
end | |||||
test "closes report", %{conn: conn, id: id, admin: admin} do | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "closed", "id" => id} | |||||
] | |||||
}) | |||||
|> json_response_and_validate_schema(:no_content) | |||||
activity = Activity.get_by_id(id) | |||||
assert activity.data["state"] == "closed" | |||||
log_entry = Repo.one(ModerationLog) | |||||
assert ModerationLog.get_log_entry_message(log_entry) == | |||||
"@#{admin.nickname} updated report ##{id} with 'closed' state" | |||||
end | |||||
test "returns 400 when state is unknown", %{conn: conn, id: id} do | |||||
conn = | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "test", "id" => id} | |||||
] | |||||
}) | |||||
assert "Unsupported state" = | |||||
hd(json_response_and_validate_schema(conn, :bad_request))["error"] | |||||
end | |||||
test "returns 404 when report is not exist", %{conn: conn} do | |||||
conn = | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "closed", "id" => "test"} | |||||
] | |||||
}) | |||||
assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found" | |||||
end | |||||
test "updates state of multiple reports", %{ | |||||
conn: conn, | |||||
id: id, | |||||
admin: admin, | |||||
second_report_id: second_report_id | |||||
} do | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> patch("/api/pleroma/admin/reports", %{ | |||||
"reports" => [ | |||||
%{"state" => "resolved", "id" => id}, | |||||
%{"state" => "closed", "id" => second_report_id} | |||||
] | |||||
}) | |||||
|> json_response_and_validate_schema(:no_content) | |||||
activity = Activity.get_by_id(id) | |||||
second_activity = Activity.get_by_id(second_report_id) | |||||
assert activity.data["state"] == "resolved" | |||||
assert second_activity.data["state"] == "closed" | |||||
[first_log_entry, second_log_entry] = Repo.all(ModerationLog) | |||||
assert ModerationLog.get_log_entry_message(first_log_entry) == | |||||
"@#{admin.nickname} updated report ##{id} with 'resolved' state" | |||||
assert ModerationLog.get_log_entry_message(second_log_entry) == | |||||
"@#{admin.nickname} updated report ##{second_report_id} with 'closed' state" | |||||
end | |||||
end | |||||
describe "GET /api/pleroma/admin/reports" do | |||||
test "returns empty response when no reports created", %{conn: conn} do | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports") | |||||
|> json_response_and_validate_schema(:ok) | |||||
assert Enum.empty?(response["reports"]) | |||||
assert response["total"] == 0 | |||||
end | |||||
test "returns reports", %{conn: conn} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports") | |||||
|> json_response_and_validate_schema(:ok) | |||||
[report] = response["reports"] | |||||
assert length(response["reports"]) == 1 | |||||
assert report["id"] == report_id | |||||
assert response["total"] == 1 | |||||
end | |||||
test "returns reports with specified state", %{conn: conn} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: first_report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
{:ok, %{id: second_report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I don't like this user" | |||||
}) | |||||
CommonAPI.update_report_state(second_report_id, "closed") | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports?state=open") | |||||
|> json_response_and_validate_schema(:ok) | |||||
assert [open_report] = response["reports"] | |||||
assert length(response["reports"]) == 1 | |||||
assert open_report["id"] == first_report_id | |||||
assert response["total"] == 1 | |||||
response = | |||||
conn | |||||
|> get("/api/pleroma/admin/reports?state=closed") | |||||
|> json_response_and_validate_schema(:ok) | |||||
assert [closed_report] = response["reports"] | |||||
assert length(response["reports"]) == 1 | |||||
assert closed_report["id"] == second_report_id | |||||
assert response["total"] == 1 | |||||
assert %{"total" => 0, "reports" => []} == | |||||
conn | |||||
|> get("/api/pleroma/admin/reports?state=resolved", %{ | |||||
"" => "" | |||||
}) | |||||
|> json_response_and_validate_schema(:ok) | |||||
end | |||||
test "returns 403 when requested by a non-admin" do | |||||
user = insert(:user) | |||||
token = insert(:oauth_token, user: user) | |||||
conn = | |||||
build_conn() | |||||
|> assign(:user, user) | |||||
|> assign(:token, token) | |||||
|> get("/api/pleroma/admin/reports") | |||||
assert json_response(conn, :forbidden) == | |||||
%{"error" => "User is not an admin or OAuth admin scope is not granted."} | |||||
end | |||||
test "returns 403 when requested by anonymous" do | |||||
conn = get(build_conn(), "/api/pleroma/admin/reports") | |||||
assert json_response(conn, :forbidden) == %{ | |||||
"error" => "Invalid credentials." | |||||
} | |||||
end | |||||
end | |||||
describe "POST /api/pleroma/admin/reports/:id/notes" do | |||||
setup %{conn: conn, admin: admin} do | |||||
[reporter, target_user] = insert_pair(:user) | |||||
activity = insert(:note_activity, user: target_user) | |||||
{:ok, %{id: report_id}} = | |||||
CommonAPI.report(reporter, %{ | |||||
account_id: target_user.id, | |||||
comment: "I feel offended", | |||||
status_ids: [activity.id] | |||||
}) | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ | |||||
content: "this is disgusting!" | |||||
}) | |||||
conn | |||||
|> put_req_header("content-type", "application/json") | |||||
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ | |||||
content: "this is disgusting2!" | |||||
}) | |||||
%{ | |||||
admin_id: admin.id, | |||||
report_id: report_id | |||||
} | |||||
end | |||||
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do | |||||
assert [note, _] = Repo.all(ReportNote) | |||||
assert %{ | |||||
activity_id: ^report_id, | |||||
content: "this is disgusting!", | |||||
user_id: ^admin_id | |||||
} = note | |||||
end | |||||
test "it returns reports with notes", %{conn: conn, admin: admin} do | |||||
conn = get(conn, "/api/pleroma/admin/reports") | |||||
response = json_response_and_validate_schema(conn, 200) | |||||
notes = hd(response["reports"])["notes"] | |||||
[note, _] = notes | |||||
assert note["user"]["nickname"] == admin.nickname | |||||
assert note["content"] == "this is disgusting!" | |||||
assert note["created_at"] | |||||
assert response["total"] == 1 | |||||
end | |||||
test "it deletes the note", %{conn: conn, report_id: report_id} do | |||||
assert ReportNote |> Repo.all() |> length() == 2 | |||||
assert [note, _] = Repo.all(ReportNote) | |||||
delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") | |||||
assert ReportNote |> Repo.all() |> length() == 1 | |||||
end | |||||
end | |||||
end |