diff --git a/lib/pleroma/web/api_spec/operations/instance_operation.ex b/lib/pleroma/web/api_spec/operations/instance_operation.ex
index bccea7486f..e2f3bf1085 100644
--- a/lib/pleroma/web/api_spec/operations/instance_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/instance_operation.ex
@@ -40,7 +40,7 @@ def rules_operation do
summary: "Retrieve list of instance rules",
operationId: "InstanceController.rules",
responses: %{
- 200 => Operation.response("Array of domains", "application/json", array_of_rules())
+ 200 => Operation.response("Array of rules", "application/json", array_of_rules())
}
}
end
diff --git a/lib/pleroma/web/api_spec/operations/mastodon_admin/account_operation.ex b/lib/pleroma/web/api_spec/operations/mastodon_admin/account_operation.ex
index b6d28e7d31..3ca61b5daa 100644
--- a/lib/pleroma/web/api_spec/operations/mastodon_admin/account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/mastodon_admin/account_operation.ex
@@ -20,7 +20,7 @@ def index_operation do
%Operation{
tags: ["User administration"],
summary: "View accounts by criteria",
- operationId: "Admin.AccountController.index",
+ operationId: "MastodonAdmin.AccountController.index",
description: "View accounts matching certain criteria for filtering, up to 100 at a time.",
security: [%{"oAuth" => ["admin:read:accounts"]}],
parameters:
@@ -97,7 +97,7 @@ def show_operation do
%Operation{
tags: ["User administration"],
summary: "View a specific account",
- operationId: "Admin.AccountController.show",
+ operationId: "MastodonAdmin.AccountController.show",
description: "View admin-level information about the given account.",
security: [%{"oAuth" => ["admin:read:accounts"]}],
parameters: [
@@ -115,7 +115,7 @@ def account_action_operation do
%Operation{
tags: ["User administration"],
summary: "Perform an action against an account",
- operationId: "Admin.AccountController.account_action",
+ operationId: "MastodonAdmin.AccountController.account_action",
description:
"Perform an action against an account and log this action in the moderation history.",
security: [%{"oAuth" => ["admin:write:accounts"]}],
@@ -147,7 +147,7 @@ def delete_operation do
%Operation{
tags: ["User administration"],
summary: "Delete a specific account",
- operationId: "Admin.AccountController.delete",
+ operationId: "MastodonAdmin.AccountController.delete",
description: "Delete the given account.",
security: [%{"oAuth" => ["admin:write:accounts"]}],
parameters: [
@@ -165,7 +165,7 @@ def enable_operation do
%Operation{
tags: ["User administration"],
summary: "Re-enable account",
- operationId: "Admin.AccountController.enable",
+ operationId: "MastodonAdmin.AccountController.enable",
description: "Re-enable a local account whose login is currently disabled.",
security: [%{"oAuth" => ["admin:write:accounts"]}],
parameters: [
@@ -183,7 +183,7 @@ def unsensitive_operation do
%Operation{
tags: ["User administration"],
summary: "Unsensitive account",
- operationId: "Admin.AccountController.unsensitive",
+ operationId: "MastodonAdmin.AccountController.unsensitive",
description: "Unsensitive a currently sensitized account.",
security: [%{"oAuth" => ["admin:write:accounts"]}],
parameters: [
@@ -201,7 +201,7 @@ def unsilence_operation do
%Operation{
tags: ["User administration"],
summary: "Unsilence account",
- operationId: "Admin.AccountController.unsilence",
+ operationId: "MastodonAdmin.AccountController.unsilence",
description: "Unsilence a currently silenced account.",
parameters: [
Operation.parameter(:id, :path, :string, "ID of the account")
@@ -218,7 +218,7 @@ def unsuspend_operation do
%Operation{
tags: ["User administration"],
summary: "Unsuspend account",
- operationId: "Admin.AccountController.unsuspend",
+ operationId: "MastodonAdmin.AccountController.unsuspend",
description: "Unsuspend a currently suspended account.",
parameters: [
Operation.parameter(:id, :path, :string, "ID of the account")
@@ -235,7 +235,7 @@ def approve_operation do
%Operation{
tags: ["User administration"],
summary: "Approve pending account",
- operationId: "Admin.AccountController.approve",
+ operationId: "MastodonAdmin.AccountController.approve",
description: "Approve the given local account if it is currently pending approval.",
parameters: [
Operation.parameter(:id, :path, :string, "ID of the account")
@@ -252,7 +252,7 @@ def reject_operation do
%Operation{
tags: ["User administration"],
summary: "Reject pending account",
- operationId: "Admin.AccountController.reject",
+ operationId: "MastodonAdmin.AccountController.reject",
description: "Reject the given local account if it is currently pending approval.",
parameters: [
Operation.parameter(:id, :path, :string, "ID of the account")
@@ -266,7 +266,7 @@ def reject_operation do
}
end
- defp account do
+ def account do
%Schema{
title: "AdminAccount",
description: "Admin-level information about a given account.",
diff --git a/lib/pleroma/web/api_spec/operations/mastodon_admin/report_operation.ex b/lib/pleroma/web/api_spec/operations/mastodon_admin/report_operation.ex
new file mode 100644
index 0000000000..a89010a4dd
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/mastodon_admin/report_operation.ex
@@ -0,0 +1,136 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.MastodonAdmin.ReportOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.MastodonAdmin.AccountOperation
+ 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: ["Report methods"],
+ summary: "View all reports",
+ operationId: "MastodonAdmin.ReportController.index",
+ description:
+ "View all reports. Pagination may be done with HTTP Link header in the response.",
+ security: [%{"oAuth" => ["admin:read:reports"]}],
+ parameters:
+ [
+ Operation.parameter(:resolved, :query, :boolean, "Filter for resolved reports"),
+ Operation.parameter(:account_id, :query, :string, "Filter by author account id"),
+ Operation.parameter(
+ :target_account_id,
+ :query,
+ :string,
+ "Filter by report target account id (not implemented)"
+ )
+ ] ++
+ pagination_params(),
+ responses: %{
+ 200 =>
+ Operation.response("Account", "application/json", %Schema{
+ title: "ArrayOfReports",
+ type: :array,
+ items: report()
+ }),
+ 401 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def show_operation do
+ %Operation{
+ tags: ["Report methods"],
+ summary: "View a single report",
+ operationId: "MastodonAdmin.ReportController.show",
+ description: "View information about the report with the given ID.",
+ security: [%{"oAuth" => ["admin:read:reports"]}],
+ parameters: [
+ Operation.parameter(:id, :path, :string, "ID of the report")
+ ],
+ responses: %{
+ 200 => Operation.response("Account", "application/json", report()),
+ 401 => Operation.response("Error", "application/json", ApiError),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def resolve_operation do
+ %Operation{
+ tags: ["Report methods"],
+ summary: "Mark as resolved",
+ operationId: "MastodonAdmin.ReportController.resolve",
+ description: "Mark a report as resolved with no further action taken.",
+ security: [%{"oAuth" => ["admin:write:reports"]}],
+ parameters: [
+ Operation.parameter(:id, :path, :string, "ID of the report")
+ ],
+ responses: %{
+ 200 => Operation.response("Account", "application/json", report()),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 401 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def reopen_operation do
+ %Operation{
+ tags: ["Report methods"],
+ summary: "Re-open report",
+ operationId: "MastodonAdmin.ReportController.reopen",
+ description: "Reopen a currently closed report.",
+ security: [%{"oAuth" => ["admin:write:reports"]}],
+ parameters: [
+ Operation.parameter(:id, :path, :string, "ID of the report")
+ ],
+ responses: %{
+ 200 => Operation.response("Account", "application/json", report()),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 401 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp report do
+ %Schema{
+ title: "Report",
+ type: :object,
+ properties: %{
+ id: FlakeID,
+ action_taken: %Schema{type: :boolean},
+ category: %Schema{type: :string},
+ comment: %Schema{type: :string},
+ created_at: %Schema{type: :string, format: "date-time"},
+ updated_at: %Schema{type: :string, format: "date-time"},
+ account: AccountOperation.account(),
+ target_account: AccountOperation.account(),
+ statuses: %Schema{
+ type: :array,
+ items: Status
+ },
+ rules: %Schema{
+ type: :array,
+ items: %Schema{
+ type: :object,
+ properties: %{
+ id: %Schema{type: :integer},
+ text: %Schema{type: :string}
+ }
+ }
+ }
+ }
+ }
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/admin/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/admin/controllers/report_controller.ex
new file mode 100644
index 0000000000..8099e1c4cf
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/admin/controllers/report_controller.ex
@@ -0,0 +1,102 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.Admin.ReportController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper,
+ only: [
+ add_link_headers: 2,
+ json_response: 3
+ ]
+
+ alias Pleroma.Activity
+ alias Pleroma.Pagination
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.AdminAPI.Report
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+ plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show])
+
+ plug(OAuthScopesPlug, %{scopes: ["admin:write:reports"]} when action in [:resolve, :reopen])
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MastodonAdmin.ReportOperation
+
+ def index(conn, params) do
+ opts =
+ %{}
+ |> Map.put(:type, "Flag")
+ |> Map.put(:skip_preload, true)
+ |> Map.put(:preload_report_notes, true)
+ |> Map.put(:total, true)
+ |> restrict_state(params)
+ |> restrict_actor(params)
+
+ # |> restrict_target(params)
+
+ reports =
+ ActivityPub.fetch_activities_query([], opts)
+ |> Pagination.fetch_paginated(params)
+
+ conn
+ |> add_link_headers(reports)
+ |> render("index.json", reports: reports)
+ end
+
+ def show(conn, %{id: id}) do
+ with %Activity{} = report <- Activity.get_report(id) do
+ render(conn, "show.json", Report.extract_report_info(report))
+ else
+ _ -> {:error, :not_found}
+ end
+ end
+
+ def resolve(conn, %{id: id}) do
+ with {:ok, report} <- CommonAPI.update_report_state(id, "resolved") do
+ render(conn, "show.json", Report.extract_report_info(report))
+ else
+ {:error, error} ->
+ json_response(conn, :bad_request, %{error: error})
+ end
+ end
+
+ def reopen(conn, %{id: id}) do
+ with {:ok, report} <- CommonAPI.update_report_state(id, "open") do
+ render(conn, "show.json", Report.extract_report_info(report))
+ else
+ {:error, error} ->
+ json_response(conn, :bad_request, %{error: error})
+ end
+ end
+
+ defp restrict_state(opts, %{resolved: true}), do: Map.put(opts, :state, "resolved")
+
+ defp restrict_state(opts, %{resolved: false}), do: Map.put(opts, :state, "open")
+
+ defp restrict_state(opts, _params), do: opts
+
+ defp restrict_actor(opts, %{account_id: actor}) do
+ with %User{ap_id: ap_id} <- User.get_by_id(actor) do
+ Map.put(opts, :actor_id, ap_id)
+ else
+ _ -> Map.put(opts, :actor_id, actor)
+ end
+ end
+
+ defp restrict_actor(opts, _params), do: opts
+
+ # defp restrict_target(opts, %{target_account_id: target}) do
+ # with %User{id: id} <- User.get_by_ap_id(target) do
+ # Map.put(opts, :user_actor_id, id)
+ # else
+ # _ -> Map.put(opts, :user_actor_id, target)
+ # end
+ # end
+
+ # defp restrict_target(opts, _params), do: opts
+end
diff --git a/lib/pleroma/web/mastodon_api/admin/views/report_view.ex b/lib/pleroma/web/mastodon_api/admin/views/report_view.ex
new file mode 100644
index 0000000000..dbcb3392f3
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/admin/views/report_view.ex
@@ -0,0 +1,59 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.Admin.ReportView do
+ use Pleroma.Web, :view
+
+ alias Pleroma.HTML
+ alias Pleroma.User
+ alias Pleroma.Web.AdminAPI.Report
+ alias Pleroma.Web.CommonAPI.Utils
+ alias Pleroma.Web.MastodonAPI
+ alias Pleroma.Web.MastodonAPI.Admin.AccountView
+ alias Pleroma.Web.MastodonAPI.InstanceView
+ alias Pleroma.Web.MastodonAPI.StatusView
+
+ def render("index.json", %{reports: reports}) do
+ reports
+ |> Enum.map(&Report.extract_report_info/1)
+ |> Enum.map(&render(__MODULE__, "show.json", &1))
+
+ # |> render_many(__MODULE__, "show.json", as: :report)
+ end
+
+ def render("show.json", %{
+ report: report,
+ user: account,
+ account: target_account,
+ statuses: statuses
+ }) do
+ created_at = Utils.to_masto_date(report.data["published"])
+
+ content =
+ unless is_nil(report.data["content"]) do
+ HTML.filter_tags(report.data["content"])
+ else
+ nil
+ end
+
+ %{
+ id: report.id,
+ action_taken: report.data["state"] != "open",
+ category: "other",
+ comment: content,
+ created_at: created_at,
+ updated_at: created_at,
+ account: AccountView.render("show.json", %{user: account}),
+ target_account: AccountView.render("show.json", %{user: target_account}),
+ assigned_account: nil,
+ action_taken_by_account: nil,
+ statuses:
+ StatusView.render("index.json", %{
+ activities: statuses,
+ as: :activity
+ }),
+ rules: []
+ }
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 792c4a8c0f..6587cf4aa5 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -313,6 +313,11 @@ defmodule Pleroma.Web.Router do
# post("/accounts/:id/unsuspend", AccountController, :unsuspend)
post("/accounts/:id/approve", AccountController, :approve)
post("/accounts/:id/reject", AccountController, :reject)
+
+ get("/reports", ReportController, :index)
+ get("/reports/:id", ReportController, :show)
+ post("/reports/:id/resolve", ReportController, :resolve)
+ post("/reports/:id/reopen", ReportController, :reopen)
end
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
diff --git a/test/pleroma/web/mastodon_api/admin/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/admin/controllers/account_controller_test.exs
index 91a13ef164..c1414059a0 100644
--- a/test/pleroma/web/mastodon_api/admin/controllers/account_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/admin/controllers/account_controller_test.exs
@@ -138,9 +138,10 @@ test "reject account", %{conn: conn} do
test "do not allow rejecting already accepted accounts", %{conn: conn} do
%{id: id} = user = insert(:user, is_approved: true)
- assert %{"error" => "User is approved"} == conn
- |> post("/api/v1/admin/accounts/#{id}/reject")
- |> json_response_and_validate_schema(400)
+ assert %{"error" => "User is approved"} ==
+ conn
+ |> post("/api/v1/admin/accounts/#{id}/reject")
+ |> json_response_and_validate_schema(400)
user = Repo.reload!(user)