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 | |||
{ | |||
"totalReports" : 1, | |||
"total" : 1, | |||
"reports": [ | |||
{ | |||
"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 | |||
- 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 | |||
@@ -740,6 +740,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do | |||
def get_reports(params, page, page_size) do | |||
params = | |||
params | |||
|> Map.new(fn {key, value} -> {to_string(key), value} end) | |||
|> Map.put("type", "Flag") | |||
|> Map.put("skip_preload", 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] | |||
alias Pleroma.Activity | |||
alias Pleroma.Config | |||
alias Pleroma.ConfigDB | |||
alias Pleroma.MFA | |||
alias Pleroma.ModerationLog | |||
alias Pleroma.Plugs.OAuthScopesPlug | |||
alias Pleroma.ReportNote | |||
alias Pleroma.Stats | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.ActivityPub.Builder | |||
alias Pleroma.Web.ActivityPub.Pipeline | |||
alias Pleroma.Web.ActivityPub.Relay | |||
alias Pleroma.Web.ActivityPub.Utils | |||
alias Pleroma.Web.AdminAPI | |||
alias Pleroma.Web.AdminAPI.AccountView | |||
alias Pleroma.Web.AdminAPI.ConfigView | |||
alias Pleroma.Web.AdminAPI.ModerationLogView | |||
alias Pleroma.Web.AdminAPI.Report | |||
alias Pleroma.Web.AdminAPI.ReportView | |||
alias Pleroma.Web.AdminAPI.Search | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.Endpoint | |||
alias Pleroma.Web.Router | |||
@@ -73,18 +67,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||
plug( | |||
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} | |||
when action in [:list_user_statuses, :list_instance_statuses] | |||
) | |||
@@ -645,85 +627,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||
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 | |||
{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 | |||
defp admin_account do | |||
def admin_account do | |||
%Schema{ | |||
type: :object, | |||
properties: %{ | |||
@@ -183,11 +183,11 @@ defmodule Pleroma.Web.Router do | |||
patch("/users/confirm_email", AdminAPIController, :confirm_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) | |||
put("/statuses/:id", StatusController, :update) | |||
@@ -17,7 +17,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
alias Pleroma.MFA | |||
alias Pleroma.ModerationLog | |||
alias Pleroma.Repo | |||
alias Pleroma.ReportNote | |||
alias Pleroma.Tests.ObanHelpers | |||
alias Pleroma.User | |||
alias Pleroma.Web | |||
@@ -1198,286 +1197,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
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 | |||
setup do: clear_config(:configurable_from_database, true) | |||
@@ -3195,66 +2914,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
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 | |||
test "structure", %{conn: conn} do | |||
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 |