MastoAPI: /api/v1/admin/accounts, /api/v1/admin/reports
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
parent
222306ff27
commit
0e0931a3c2
14 changed files with 1230 additions and 1 deletions
1
changelog.d/mastodon-admin.add
Normal file
1
changelog.d/mastodon-admin.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Implement basics of Mastodon admin API
|
||||||
|
|
@ -346,6 +346,16 @@ defmodule Pleroma.ModerationLog do
|
||||||
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "reject",
|
||||||
|
"subject" => users
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} rejected users: #{users_to_nicknames_string(users)}"
|
||||||
|
end
|
||||||
|
|
||||||
def get_log_entry_message(%ModerationLog{
|
def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
|
|
||||||
|
|
@ -1964,6 +1964,12 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
def approve(%User{} = user), do: {:ok, user}
|
def approve(%User{} = user), do: {:ok, user}
|
||||||
|
|
||||||
|
def reject(%User{is_approved: false} = user) do
|
||||||
|
delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject(%User{} = _user), do: {:error, "User is approved"}
|
||||||
|
|
||||||
def confirm(users) when is_list(users) do
|
def confirm(users) when is_list(users) do
|
||||||
Repo.transaction(fn ->
|
Repo.transaction(fn ->
|
||||||
Enum.map(users, fn user ->
|
Enum.map(users, fn user ->
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,8 @@ defmodule Pleroma.User.Query do
|
||||||
limit: pos_integer(),
|
limit: pos_integer(),
|
||||||
actor_types: [String.t()],
|
actor_types: [String.t()],
|
||||||
birthday_day: pos_integer(),
|
birthday_day: pos_integer(),
|
||||||
birthday_month: pos_integer()
|
birthday_month: pos_integer(),
|
||||||
|
staff: boolean()
|
||||||
}
|
}
|
||||||
| map()
|
| map()
|
||||||
|
|
||||||
|
|
@ -179,6 +180,10 @@ defmodule Pleroma.User.Query do
|
||||||
|
|
||||||
defp compose_query({:external, _}, query), do: location_query(query, false)
|
defp compose_query({:external, _}, query), do: location_query(query, false)
|
||||||
|
|
||||||
|
defp compose_query({:active, false}, query) do
|
||||||
|
where(query, [u], u.is_active == false or u.is_approved == false or u.is_confirmed == false)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({:active, _}, query) do
|
defp compose_query({:active, _}, query) do
|
||||||
where(query, [u], u.is_active == true)
|
where(query, [u], u.is_active == true)
|
||||||
|> where([u], u.is_approved == true)
|
|> where([u], u.is_approved == true)
|
||||||
|
|
@ -202,6 +207,10 @@ defmodule Pleroma.User.Query do
|
||||||
where(query, [u], u.is_confirmed != ^bool)
|
where(query, [u], u.is_confirmed != ^bool)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:need_approval, false}, query) do
|
||||||
|
where(query, [u], u.is_approved == true)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({:need_approval, _}, query) do
|
defp compose_query({:need_approval, _}, query) do
|
||||||
where(query, [u], u.is_approved == false)
|
where(query, [u], u.is_approved == false)
|
||||||
end
|
end
|
||||||
|
|
@ -285,6 +294,14 @@ defmodule Pleroma.User.Query do
|
||||||
|> where([u], fragment("date_part('month', ?)", u.birthday) == ^month)
|
|> where([u], fragment("date_part('month', ?)", u.birthday) == ^month)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:staff, false}, query) do
|
||||||
|
where(query, [u], u.is_admin == false and u.is_moderator == false)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compose_query({:staff, _}, query) do
|
||||||
|
where(query, [u], u.is_admin == true or u.is_moderator == true)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query(_unsupported_param, query), do: query
|
defp compose_query(_unsupported_param, query), do: query
|
||||||
|
|
||||||
defp location_query(query, local) do
|
defp location_query(query, local) do
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,13 @@ defmodule Pleroma.Web.ApiSpec do
|
||||||
"Announcement management"
|
"Announcement management"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
"name" => "Administration (Mastodon API)",
|
||||||
|
"tags" => [
|
||||||
|
"User administration (Mastodon API)",
|
||||||
|
"Report management (Mastodon API)"
|
||||||
|
]
|
||||||
|
},
|
||||||
%{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]},
|
%{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]},
|
||||||
%{
|
%{
|
||||||
"name" => "Current account",
|
"name" => "Current account",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.MastodonAdmin.AccountOperation 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
|
||||||
|
|
||||||
|
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: ["User administration (Mastodon API)"],
|
||||||
|
summary: "View accounts by criteria",
|
||||||
|
operationId: "MastodonAdmin.AccountController.index",
|
||||||
|
description: "View accounts matching certain criteria for filtering, up to 40 at a time.",
|
||||||
|
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
||||||
|
parameters:
|
||||||
|
[
|
||||||
|
Operation.parameter(:local, :query, :boolean, "Filter for local accounts?"),
|
||||||
|
Operation.parameter(:remote, :query, :boolean, "Filter for remote accounts?"),
|
||||||
|
Operation.parameter(
|
||||||
|
:by_domain,
|
||||||
|
:query,
|
||||||
|
:string,
|
||||||
|
"Filter by the given domain (not implemented yet)"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:active,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Filter for currently active accounts??"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:pending,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Filter for currently pending accounts?"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:disabled,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Filter for currently disabled accounts?"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:sensitized,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Filter for currently sensitized accounts? (not implemented yet)"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:silenced,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Filter for currently silenced accounts? (not implemented yet)"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:suspended,
|
||||||
|
:query,
|
||||||
|
:boolean,
|
||||||
|
"Filter for currently suspended accounts? (not implemented yet)"
|
||||||
|
),
|
||||||
|
Operation.parameter(:username, :query, :string, "Username to search for"),
|
||||||
|
Operation.parameter(:display_name, :query, :string, "Display name to search for"),
|
||||||
|
Operation.parameter(:email, :query, :string, "Lookup a user with this email"),
|
||||||
|
Operation.parameter(
|
||||||
|
:ip,
|
||||||
|
:query,
|
||||||
|
:string,
|
||||||
|
"Lookup users by this IP address (not implemented yet)"
|
||||||
|
),
|
||||||
|
Operation.parameter(:staff, :query, :boolean, "Filter for staff accounts?")
|
||||||
|
] ++
|
||||||
|
pagination_params(),
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Account", "application/json", %Schema{
|
||||||
|
title: "ArrayOfAccounts",
|
||||||
|
type: :array,
|
||||||
|
items: account()
|
||||||
|
}),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration (Mastodon API)"],
|
||||||
|
summary: "View a specific account",
|
||||||
|
operationId: "MastodonAdmin.AccountController.show",
|
||||||
|
description: "View admin-level information about the given account.",
|
||||||
|
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", account()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_action_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration (Mastodon API)"],
|
||||||
|
summary: "Perform an action against an account",
|
||||||
|
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"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
type: %Schema{
|
||||||
|
type: :string,
|
||||||
|
enum: ["none", "disable", "sensitive", "silence", "suspend"]
|
||||||
|
},
|
||||||
|
report_id: %Schema{
|
||||||
|
type: :string,
|
||||||
|
nullable: true,
|
||||||
|
description: "ID of an associated report that caused this action to be taken"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
204 => no_content_response(),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration (Mastodon API)"],
|
||||||
|
summary: "Delete a specific account",
|
||||||
|
operationId: "MastodonAdmin.AccountController.delete",
|
||||||
|
description: "Delete the given account.",
|
||||||
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", account()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration (Mastodon API)"],
|
||||||
|
summary: "Re-enable account",
|
||||||
|
operationId: "MastodonAdmin.AccountController.enable",
|
||||||
|
description: "Re-enable a local account whose login is currently disabled.",
|
||||||
|
security: [%{"oAuth" => ["admin:write:accounts"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "ID of the account")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", account()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration (Mastodon API)"],
|
||||||
|
summary: "Approve pending account",
|
||||||
|
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")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", account()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["User administration (Mastodon API)"],
|
||||||
|
summary: "Reject pending account",
|
||||||
|
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")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", account()),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def account do
|
||||||
|
%Schema{
|
||||||
|
title: "Admin::Account",
|
||||||
|
description: "Admin-level information about a given account.",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: FlakeID,
|
||||||
|
username: %Schema{type: :string},
|
||||||
|
domain: %Schema{type: :string, nullable: true},
|
||||||
|
created_at: %Schema{type: :string, format: "date-time"},
|
||||||
|
email: %Schema{type: :string, format: "email", nullable: true},
|
||||||
|
ip: %Schema{type: :string, nullable: true},
|
||||||
|
ips: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
ip: %Schema{type: :string},
|
||||||
|
used_at: %Schema{type: :string, format: "date-time"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
locale: %Schema{type: :string, format: "date-time", nullable: true},
|
||||||
|
invite_request: %Schema{type: :string, format: "date-time", nullable: true},
|
||||||
|
role: %Schema{type: :string, nullable: true},
|
||||||
|
confirmed: %Schema{type: :boolean},
|
||||||
|
approved: %Schema{type: :boolean},
|
||||||
|
disabled: %Schema{type: :boolean},
|
||||||
|
silenced: %Schema{type: :boolean, nullable: true},
|
||||||
|
suspended: %Schema{type: :boolean, nullable: true},
|
||||||
|
account: Account,
|
||||||
|
created_by_application_id: %Schema{type: :string, nullable: true},
|
||||||
|
invited_by_account_id: %Schema{type: :string, nullable: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# 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 management (Mastodon API)"],
|
||||||
|
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("Reports", "application/json", %Schema{
|
||||||
|
title: "ArrayOfReports",
|
||||||
|
type: :array,
|
||||||
|
items: report()
|
||||||
|
}),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Report management (Mastodon API)"],
|
||||||
|
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("Report", "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 management (Mastodon API)"],
|
||||||
|
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("Report", "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 management (Mastodon API)"],
|
||||||
|
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("Report", "application/json", report()),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report do
|
||||||
|
%Schema{
|
||||||
|
title: "Admin::Report",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: FlakeID,
|
||||||
|
action_taken: %Schema{type: :boolean},
|
||||||
|
action_taken_at: %Schema{type: :string, format: "date-time", nullable: true},
|
||||||
|
category: %Schema{type: :string, enum: ["spam", "violation", "other"]},
|
||||||
|
comment: %Schema{type: :string, nullable: true},
|
||||||
|
forwarded: %Schema{type: :boolean, nullable: true},
|
||||||
|
created_at: %Schema{type: :string, format: "date-time"},
|
||||||
|
updated_at: %Schema{type: :string, format: "date-time"},
|
||||||
|
account: AccountOperation.account(),
|
||||||
|
target_account: AccountOperation.account(),
|
||||||
|
assigned_account: %Schema{
|
||||||
|
oneOf: [
|
||||||
|
AccountOperation.account(),
|
||||||
|
%Schema{type: :null}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
action_taken_by_account: %Schema{
|
||||||
|
oneOf: [
|
||||||
|
AccountOperation.account(),
|
||||||
|
%Schema{type: :null}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,220 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.AccountController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper,
|
||||||
|
only: [
|
||||||
|
add_link_headers: 2,
|
||||||
|
assign_account_by_id: 2,
|
||||||
|
json_response: 3
|
||||||
|
]
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Pagination
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
@filter_params ~W(
|
||||||
|
local external active needing_approval deactivated nickname name email staff
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["admin:read:accounts"]}
|
||||||
|
when action in [:index, :show]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["admin:write:accounts"]}
|
||||||
|
when action in [
|
||||||
|
:delete,
|
||||||
|
:enable,
|
||||||
|
:account_action,
|
||||||
|
:approve,
|
||||||
|
:reject
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:assign_account_by_id
|
||||||
|
when action in [
|
||||||
|
:show,
|
||||||
|
:delete,
|
||||||
|
:enable,
|
||||||
|
:account_action,
|
||||||
|
:approve,
|
||||||
|
:reject
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MastodonAdmin.AccountOperation
|
||||||
|
|
||||||
|
def index(conn, params) do
|
||||||
|
users =
|
||||||
|
params
|
||||||
|
|> build_criteria()
|
||||||
|
|> User.Query.build()
|
||||||
|
|> Pagination.fetch_paginated(params)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(users)
|
||||||
|
|> render("index.json", users: users)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{user: _admin, account: user}} = conn, _params) do
|
||||||
|
render(conn, "show.json", user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_action(
|
||||||
|
%{assigns: %{user: admin, account: user}, body_params: %{type: type} = body_params} =
|
||||||
|
conn,
|
||||||
|
_params
|
||||||
|
) do
|
||||||
|
{:ok, _user} = handle_account_action(user, admin, type)
|
||||||
|
|
||||||
|
resolve_report(admin, body_params)
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(%{assigns: %{user: admin, account: user}} = conn, _params) do
|
||||||
|
{:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
|
||||||
|
Pipeline.common_pipeline(delete_data, local: true)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "delete"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "show.json", user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable(%{assigns: %{user: admin, account: user}} = conn, _params) do
|
||||||
|
{:ok, user} = User.set_activation(user, true)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "activate"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "show.json", user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve(%{assigns: %{user: admin, account: user}} = conn, _params) do
|
||||||
|
{:ok, user} = User.approve(user)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "approve"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "show.json", user: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject(%{assigns: %{user: admin, account: user}} = conn, _params) do
|
||||||
|
with {:ok, _} <- User.reject(user) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "reject"
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "show.json", user: user)
|
||||||
|
else
|
||||||
|
{:error, error} ->
|
||||||
|
json_response(conn, :bad_request, %{error: error})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_account_action(%User{local: true} = user, admin, "disable") do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: "deactivate"
|
||||||
|
})
|
||||||
|
|
||||||
|
User.set_activation(user, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp handle_account_action(user, _admin, _type) do
|
||||||
|
{:ok, user}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_criteria(params) do
|
||||||
|
%{}
|
||||||
|
|> maybe_filter_local(params)
|
||||||
|
|> maybe_filter_external(params)
|
||||||
|
|> maybe_filter_active(params)
|
||||||
|
|> maybe_filter_needing_approval(params)
|
||||||
|
|> maybe_filter_deactivated(params)
|
||||||
|
|> maybe_filter_nickname(params)
|
||||||
|
|> maybe_filter_name(params)
|
||||||
|
|> maybe_filter_email(params)
|
||||||
|
|> maybe_filter_staff(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_filter_local(criteria, %{local: true} = _params),
|
||||||
|
do: Map.put(criteria, :local, true)
|
||||||
|
|
||||||
|
defp maybe_filter_local(criteria, %{local: false} = _params),
|
||||||
|
do: Map.put(criteria, :external, true)
|
||||||
|
|
||||||
|
defp maybe_filter_external(criteria, %{remote: true} = _params),
|
||||||
|
do: Map.put(criteria, :external, true)
|
||||||
|
|
||||||
|
defp maybe_filter_external(criteria, %{remote: false} = _params),
|
||||||
|
do: Map.put(criteria, :local, true)
|
||||||
|
|
||||||
|
defp maybe_filter_active(criteria, %{active: active} = _params),
|
||||||
|
do: Map.put(criteria, :active, active)
|
||||||
|
|
||||||
|
defp maybe_filter_needing_approval(criteria, %{pending: need_approval} = _params),
|
||||||
|
do: Map.put(criteria, :need_approval, need_approval)
|
||||||
|
|
||||||
|
defp maybe_filter_deactivated(criteria, %{disabled: deactivated} = _params),
|
||||||
|
do: Map.put(criteria, :deactivated, deactivated)
|
||||||
|
|
||||||
|
defp maybe_filter_nickname(criteria, %{username: nickname} = _params),
|
||||||
|
do: Map.put(criteria, :nickname, nickname)
|
||||||
|
|
||||||
|
defp maybe_filter_name(criteria, %{display_name: name} = _params),
|
||||||
|
do: Map.put(criteria, :name, name)
|
||||||
|
|
||||||
|
defp maybe_filter_email(criteria, %{email: email} = _params),
|
||||||
|
do: Map.put(criteria, :email, email)
|
||||||
|
|
||||||
|
defp maybe_filter_staff(criteria, %{staff: staff} = _params),
|
||||||
|
do: Map.put(criteria, :staff, staff)
|
||||||
|
|
||||||
|
for filter_param <- @filter_params do
|
||||||
|
defp unquote(:"maybe_filter_#{filter_param}")(criteria, _params), do: criteria
|
||||||
|
end
|
||||||
|
|
||||||
|
defp resolve_report(admin, %{report_id: id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.update_report_state(id, "resolved"),
|
||||||
|
report <- Activity.get_by_id_with_user_actor(activity.id) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity,
|
||||||
|
subject_actor: report.user_actor
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp resolve_report(_admin, _params) do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# 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.ModerationLog
|
||||||
|
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)
|
||||||
|
|
||||||
|
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(%{assigns: %{user: admin}} = conn, %{id: id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.update_report_state(id, "resolved"),
|
||||||
|
report <- Activity.get_by_id_with_user_actor(activity.id) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity,
|
||||||
|
subject_actor: report.user_actor
|
||||||
|
})
|
||||||
|
|
||||||
|
render(conn, "show.json", Report.extract_report_info(activity))
|
||||||
|
else
|
||||||
|
{:error, error} ->
|
||||||
|
json_response(conn, :bad_request, %{error: error})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def reopen(%{assigns: %{user: admin}} = conn, %{id: id}) do
|
||||||
|
with {:ok, activity} <- CommonAPI.update_report_state(id, "open"),
|
||||||
|
report <- Activity.get_by_id_with_user_actor(activity.id) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity,
|
||||||
|
subject_actor: report.user_actor
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
64
lib/pleroma/web/mastodon_api/admin/views/account_view.ex
Normal file
64
lib/pleroma/web/mastodon_api/admin/views/account_view.ex
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.AccountView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
|
||||||
|
def render("index.json", %{users: users}) do
|
||||||
|
render_many(users, __MODULE__, "show.json", as: :user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{user: user}) do
|
||||||
|
account =
|
||||||
|
MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: user.id,
|
||||||
|
username: username_from_nickname(user.nickname),
|
||||||
|
domain: domain_from_nickname(user.nickname),
|
||||||
|
created_at: Utils.to_masto_date(user.inserted_at),
|
||||||
|
email: user.email,
|
||||||
|
ip: nil,
|
||||||
|
ips: [],
|
||||||
|
locale: nil,
|
||||||
|
invite_request: user.registration_reason,
|
||||||
|
role: role(user),
|
||||||
|
confirmed: user.is_confirmed,
|
||||||
|
approved: user.is_approved,
|
||||||
|
disabled: !user.is_active,
|
||||||
|
silenced: nil,
|
||||||
|
suspended: nil,
|
||||||
|
account: account
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp username_from_nickname(string) when is_binary(string) do
|
||||||
|
hd(String.split(string, "@"))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp username_from_nickname(_), do: nil
|
||||||
|
|
||||||
|
defp domain_from_nickname(string) when is_binary(string) do
|
||||||
|
String.split(string, "@")
|
||||||
|
|> Enum.at(1, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp domain_from_nickname(_), do: nil
|
||||||
|
|
||||||
|
defp role(%User{is_admin: true}) do
|
||||||
|
"admin"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp role(%User{is_moderator: true}) do
|
||||||
|
"moderator"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp role(_user) do
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
56
lib/pleroma/web/mastodon_api/admin/views/report_view.ex
Normal file
56
lib/pleroma/web/mastodon_api/admin/views/report_view.ex
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.ReportView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI.Admin.AccountView
|
||||||
|
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))
|
||||||
|
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",
|
||||||
|
action_taken_at: nil,
|
||||||
|
category: "other",
|
||||||
|
comment: content,
|
||||||
|
forwarded: nil,
|
||||||
|
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
|
||||||
|
|
@ -418,6 +418,46 @@ defmodule Pleroma.Web.Router do
|
||||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Mastodon AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||||
|
scope "/api/v1/admin", Pleroma.Web.MastodonAPI.Admin do
|
||||||
|
pipe_through([:require_privileged_role_users_read])
|
||||||
|
|
||||||
|
get("/accounts", AccountController, :index)
|
||||||
|
get("/accounts/:id", AccountController, :show)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Mastodon AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||||
|
scope "/api/v1/admin", Pleroma.Web.MastodonAPI.Admin do
|
||||||
|
pipe_through(:require_privileged_role_users_delete)
|
||||||
|
|
||||||
|
delete("/accounts/:id", AccountController, :delete)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Mastodon AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||||
|
scope "/api/v1/admin", Pleroma.Web.MastodonAPI.Admin do
|
||||||
|
pipe_through([:require_privileged_role_users_manage_activation_state])
|
||||||
|
|
||||||
|
post("/accounts/:id/action", AccountController, :account_action)
|
||||||
|
post("/accounts/:id/enable", AccountController, :enable)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Mastodon AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||||
|
scope "/api/v1/admin", Pleroma.Web.MastodonAPI.Admin do
|
||||||
|
pipe_through([:require_privileged_role_users_manage_invites])
|
||||||
|
post("/accounts/:id/approve", AccountController, :approve)
|
||||||
|
post("/accounts/:id/reject", AccountController, :reject)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Mastodon AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||||
|
scope "/api/v1/admin", Pleroma.Web.MastodonAPI.Admin do
|
||||||
|
pipe_through([:require_privileged_role_reports_manage_reports])
|
||||||
|
|
||||||
|
get("/reports", ReportController, :index)
|
||||||
|
get("/reports/:id", ReportController, :show)
|
||||||
|
post("/reports/:id/resolve", ReportController, :resolve)
|
||||||
|
post("/reports/:id/reopen", ReportController, :reopen)
|
||||||
|
end
|
||||||
|
|
||||||
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||||
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
pipe_through(:require_privileged_role_emoji_manage_emoji)
|
pipe_through(:require_privileged_role_emoji_manage_emoji)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,175 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.AccountControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
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/v1/admin/accounts" do
|
||||||
|
test "search by display name", %{conn: conn} do
|
||||||
|
%{id: id} = insert(:user, name: "Display name")
|
||||||
|
insert(:user, name: "Other name")
|
||||||
|
|
||||||
|
assert [%{"id" => ^id}] =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/admin/accounts?display_name=Display")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/v1/admin/accounts/:id" do
|
||||||
|
test "show admin-level information", %{conn: conn} do
|
||||||
|
%{id: id} =
|
||||||
|
insert(:user,
|
||||||
|
email: "email@example.com",
|
||||||
|
is_confirmed: false,
|
||||||
|
is_moderator: true
|
||||||
|
)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"id" => ^id,
|
||||||
|
"email" => "email@example.com",
|
||||||
|
"confirmed" => false,
|
||||||
|
"role" => "moderator"
|
||||||
|
} =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/admin/accounts/#{id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/v1/admin/accounts/:id" do
|
||||||
|
test "delete account", %{conn: conn} do
|
||||||
|
%{id: id} = user = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/admin/accounts/#{id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
assert %{is_active: false} = user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/admin/accounts/:id/action" do
|
||||||
|
test "disable account", %{conn: conn} do
|
||||||
|
%{id: id} = user = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/admin/accounts/#{id}/action", %{
|
||||||
|
"type" => "disable"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(204)
|
||||||
|
|
||||||
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
assert %{is_active: false} = user
|
||||||
|
end
|
||||||
|
|
||||||
|
test "perform action with assigned report", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id} = report} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id
|
||||||
|
})
|
||||||
|
|
||||||
|
%{id: id} = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/admin/accounts/#{id}/action", %{
|
||||||
|
"type" => "none",
|
||||||
|
"report_id" => report_id
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(204)
|
||||||
|
|
||||||
|
report = Repo.reload!(report)
|
||||||
|
|
||||||
|
assert %{data: %{"state" => "resolved"}} = report
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/admin/accounts/:id/enable" do
|
||||||
|
test "enable account", %{conn: conn} do
|
||||||
|
%{id: id} = user = insert(:user)
|
||||||
|
User.set_activation(user, false)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/admin/accounts/#{id}/enable")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
assert %{is_active: true} = user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/admin/accounts/:id/approve" do
|
||||||
|
test "approve account", %{conn: conn} do
|
||||||
|
%{id: id} = user = insert(:user, is_approved: false)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/admin/accounts/#{id}/approve")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
assert %{is_approved: true} = user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/admin/accounts/:id/rejct" do
|
||||||
|
test "reject account", %{conn: conn} do
|
||||||
|
%{id: id} = user = insert(:user, is_approved: false)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/admin/accounts/#{id}/reject")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
assert %{is_active: false} = user
|
||||||
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
user = Repo.reload!(user)
|
||||||
|
|
||||||
|
assert %{is_approved: true} = user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.Admin.ReportControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
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/v1/admin/reports" do
|
||||||
|
test "get reports by state", %{conn: conn} do
|
||||||
|
[reporter, target_user] = insert_pair(:user)
|
||||||
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
{:ok, %{id: report_id1}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
comment: "this user uses an app called Hitler Tusky",
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, %{id: report_id2}} =
|
||||||
|
CommonAPI.report(reporter, %{
|
||||||
|
account_id: target_user.id,
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
CommonAPI.update_report_state(report_id2, "resolved")
|
||||||
|
|
||||||
|
assert [%{"id" => ^report_id1}] =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/admin/reports?resolved=false")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^report_id2}] =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/admin/reports?resolved=true")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/v1/admin/reports/:id" do
|
||||||
|
test "get report by 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,
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"id" => ^report_id} =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/admin/reports/#{report_id}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/admin/reports/:id/resolve" do
|
||||||
|
test "resolve a report", %{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,
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"id" => ^report_id, "action_taken" => true} =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/admin/reports/#{report_id}/resolve")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/admin/reports/:id/reopen" do
|
||||||
|
test "reopen a report", %{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,
|
||||||
|
status_ids: [activity.id]
|
||||||
|
})
|
||||||
|
|
||||||
|
CommonAPI.update_report_state(report_id, "resolved")
|
||||||
|
|
||||||
|
assert %{"id" => ^report_id, "action_taken" => false} =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/admin/reports/#{report_id}/reopen")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue