mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2025-04-09 20:44:08 +00:00
Merge branch 'mastodon-admin' into 'develop'
MastoAPI: /api/v1/admin/accounts, /api/v1/admin/reports See merge request pleroma/pleroma!3671
This commit is contained in:
commit
61a55984d1
14 changed files with 1459 additions and 2 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
|
|
@ -339,6 +339,16 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
||||
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{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
|
|
@ -1941,6 +1941,12 @@ defmodule Pleroma.User do
|
|||
|
||||
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
|
||||
Repo.transaction(fn ->
|
||||
Enum.map(users, fn user ->
|
||||
|
@ -2615,7 +2621,7 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
# Internal function; public one is `deactivate/2`
|
||||
# Internal function; public one is `set_activation/2`
|
||||
defp set_activation_status(user, status) do
|
||||
user
|
||||
|> cast(%{is_active: status}, [:is_active])
|
||||
|
|
|
@ -63,7 +63,8 @@ defmodule Pleroma.User.Query do
|
|||
limit: pos_integer(),
|
||||
actor_types: [String.t()],
|
||||
birthday_day: pos_integer(),
|
||||
birthday_month: pos_integer()
|
||||
birthday_month: pos_integer(),
|
||||
staff: boolean()
|
||||
}
|
||||
| map()
|
||||
|
||||
|
@ -179,6 +180,10 @@ defmodule Pleroma.User.Query do
|
|||
|
||||
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
|
||||
where(query, [u], u.is_active == true)
|
||||
|> where([u], u.is_approved == true)
|
||||
|
@ -202,6 +207,10 @@ defmodule Pleroma.User.Query do
|
|||
where(query, [u], u.is_confirmed != ^bool)
|
||||
end
|
||||
|
||||
defp compose_query({:need_approval, false}, query) do
|
||||
where(query, [u], u.is_approved == true)
|
||||
end
|
||||
|
||||
defp compose_query({:need_approval, _}, query) do
|
||||
where(query, [u], u.is_approved == false)
|
||||
end
|
||||
|
@ -285,6 +294,19 @@ defmodule Pleroma.User.Query do
|
|||
|> where([u], fragment("date_part('month', ?)", u.birthday) == ^month)
|
||||
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({:domain, domain}, query) do
|
||||
query
|
||||
|> where([u], like(u.nickname, ^"%@#{domain}"))
|
||||
end
|
||||
|
||||
defp compose_query(_unsupported_param, query), do: query
|
||||
|
||||
defp location_query(query, local) do
|
||||
|
|
|
@ -108,6 +108,13 @@ defmodule Pleroma.Web.ApiSpec do
|
|||
"Announcement management"
|
||||
]
|
||||
},
|
||||
%{
|
||||
"name" => "Administration (Mastodon API)",
|
||||
"tags" => [
|
||||
"User administration (Mastodon API)",
|
||||
"Report management (Mastodon API)"
|
||||
]
|
||||
},
|
||||
%{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]},
|
||||
%{
|
||||
"name" => "Current account",
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
# 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 (v1)",
|
||||
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(
|
||||
: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(
|
||||
: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(
|
||||
:sensitized,
|
||||
:query,
|
||||
:boolean,
|
||||
"Filter for accounts force-marked as sensitive? (not implemented yet)"
|
||||
),
|
||||
Operation.parameter(:username, :query, :string, "Search for the given username"),
|
||||
Operation.parameter(
|
||||
:display_name,
|
||||
:query,
|
||||
:string,
|
||||
"Search for the given display name"
|
||||
),
|
||||
Operation.parameter(
|
||||
:by_domain,
|
||||
:query,
|
||||
:string,
|
||||
"Filter by the given domain"
|
||||
),
|
||||
Operation.parameter(:email, :query, :string, "Lookup a user with this email"),
|
||||
Operation.parameter(
|
||||
:ip,
|
||||
:query,
|
||||
:string,
|
||||
"Lookup users with 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 index2_operation do
|
||||
%Operation{
|
||||
tags: ["User administration (Mastodon API)"],
|
||||
summary: "View accounts by criteria (v2)",
|
||||
operationId: "MastodonAdmin.AccountController.index2",
|
||||
description: "View accounts matching certain criteria for filtering, up to 40 at a time.",
|
||||
security: [%{"oAuth" => ["admin:read:accounts"]}],
|
||||
parameters:
|
||||
[
|
||||
Operation.parameter(
|
||||
:origin,
|
||||
:query,
|
||||
%Schema{type: :string, enum: ["local", "remote"]},
|
||||
"Filter for local or remote accounts"
|
||||
),
|
||||
Operation.parameter(
|
||||
:status,
|
||||
:query,
|
||||
%Schema{
|
||||
type: :string,
|
||||
enum: ["active", "inactive", "pending", "disabled", "silenced", "suspended"]
|
||||
},
|
||||
"Filter for active, pending, disabled, silenced or suspended accounts"
|
||||
),
|
||||
Operation.parameter(
|
||||
:permissions,
|
||||
:query,
|
||||
:string,
|
||||
"Filter for accounts with staff permissions (users that can manage reports). (not implemented yet)"
|
||||
),
|
||||
Operation.parameter(
|
||||
:role_ids,
|
||||
:query,
|
||||
%Schema{
|
||||
oneOf: [
|
||||
%Schema{type: :array, items: %Schema{type: :string}},
|
||||
%Schema{type: :string}
|
||||
]
|
||||
},
|
||||
"Filter for users with these roles. (not implemented yet)"
|
||||
),
|
||||
Operation.parameter(
|
||||
:invited_by,
|
||||
:query,
|
||||
:string,
|
||||
"Lookup users invited by the account with this ID. (not implemented yet)"
|
||||
),
|
||||
Operation.parameter(:username, :query, :string, "Search for the given username"),
|
||||
Operation.parameter(
|
||||
:display_name,
|
||||
:query,
|
||||
:string,
|
||||
"Search for the given display name"
|
||||
),
|
||||
Operation.parameter(
|
||||
:by_domain,
|
||||
:query,
|
||||
:string,
|
||||
"Filter by the given domain"
|
||||
),
|
||||
Operation.parameter(:email, :query, :string, "Lookup a user with this email"),
|
||||
Operation.parameter(
|
||||
:ip,
|
||||
:query,
|
||||
:string,
|
||||
"Lookup users with this IP address (not implemented yet)"
|
||||
)
|
||||
] ++
|
||||
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,146 @@
|
|||
# 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{
|
||||
type: AccountOperation.account(),
|
||||
nullable: true
|
||||
},
|
||||
action_taken_by_account: %Schema{
|
||||
type: AccountOperation.account(),
|
||||
nullable: true
|
||||
},
|
||||
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,264 @@
|
|||
# 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 domain email staff origin status
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["admin:read:accounts"]}
|
||||
when action in [:index, :index2, :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 index2(conn, params) do
|
||||
users =
|
||||
params
|
||||
|> build_criteria_v2()
|
||||
|> 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_domain(params)
|
||||
|> maybe_filter_email(params)
|
||||
|> maybe_filter_staff(params)
|
||||
end
|
||||
|
||||
defp build_criteria_v2(params) do
|
||||
%{}
|
||||
|> maybe_filter_origin(params)
|
||||
|> maybe_filter_status(params)
|
||||
|> maybe_filter_nickname(params)
|
||||
|> maybe_filter_name(params)
|
||||
|> maybe_filter_domain(params)
|
||||
|> maybe_filter_email(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_origin(criteria, %{origin: "local"} = _params),
|
||||
do: Map.put(criteria, :local, true)
|
||||
|
||||
defp maybe_filter_origin(criteria, %{origin: "remote"} = _params),
|
||||
do: Map.put(criteria, :external, 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_status(criteria, %{status: "active"} = _params),
|
||||
do: Map.put(criteria, :active, true)
|
||||
|
||||
defp maybe_filter_status(criteria, %{status: "inactive"} = _params),
|
||||
do: Map.put(criteria, :active, false)
|
||||
|
||||
defp maybe_filter_status(criteria, %{status: "pending"} = _params),
|
||||
do: Map.put(criteria, :need_approval, true)
|
||||
|
||||
defp maybe_filter_status(criteria, %{status: "disabled"} = _params),
|
||||
do: Map.put(criteria, :deactivated, true)
|
||||
|
||||
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_domain(criteria, %{by_domain: domain} = _params),
|
||||
do: Map.put(criteria, :domain, domain)
|
||||
|
||||
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
|
|
@ -416,6 +416,53 @@ defmodule Pleroma.Web.Router do
|
|||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||
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
|
||||
|
||||
# Mastodon AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||
scope "/api/v2/admin", Pleroma.Web.MastodonAPI.Admin do
|
||||
pipe_through([:require_privileged_role_users_read])
|
||||
|
||||
get("/accounts", AccountController, :index2)
|
||||
end
|
||||
|
||||
# AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
|
||||
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||
pipe_through(:require_privileged_role_emoji_manage_emoji)
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
# 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
|
||||
|
||||
# Adapted from
|
||||
# https://github.com/mastodon/mastodon/blob/main/spec/requests/api/v2/admin/accounts_spec.rb
|
||||
describe "GET /api/v2/admin/accounts" do
|
||||
setup do
|
||||
remote_account = insert(:user, nickname: "remote@example.org", local: false)
|
||||
other_remote_account = insert(:user, nickname: "other@foo.bar", local: false)
|
||||
# suspended_account = insert(:user)
|
||||
# suspended_remote = insert(:user)
|
||||
disabled_account = insert(:user, is_active: false)
|
||||
pending_account = insert(:user, is_approved: false)
|
||||
admin_account = insert(:user, is_admin: true)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
remote_account: remote_account,
|
||||
other_remote_account: other_remote_account,
|
||||
disabled_account: disabled_account,
|
||||
pending_account: pending_account,
|
||||
admin_account: admin_account
|
||||
}}
|
||||
end
|
||||
|
||||
# test "returns the correct accounts when called with status active
|
||||
# and origin local and permissions staff", %{
|
||||
# conn: conn,
|
||||
# admin_account: %{id: admin_account_id}
|
||||
# } do
|
||||
# assert [%{"id" => ^admin_account_id}] =
|
||||
# conn
|
||||
# |> get("/api/v2/admin/accounts?status=active&origin=local&permissions=staff")
|
||||
# |> json_response_and_validate_schema(200)
|
||||
# end
|
||||
|
||||
test "returns the correct accounts when called with by_domain value and origin remote", %{
|
||||
conn: conn,
|
||||
remote_account: %{id: remote_account_id}
|
||||
} do
|
||||
assert [%{"id" => ^remote_account_id}] =
|
||||
conn
|
||||
|> get("/api/v2/admin/accounts?by_domain=example.org&origin=remote")
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
|
||||
# test "returns the correct accounts when called with status suspended", %{
|
||||
# conn: conn,
|
||||
# suspended_account: %{id: suspended_account_id}
|
||||
# } do
|
||||
# assert [%{"id" => ^suspended_account_id}] =
|
||||
# conn
|
||||
# |> get("/api/v2/admin/accounts?status=suspended")
|
||||
# |> json_response_and_validate_schema(200)
|
||||
# end
|
||||
|
||||
test "returns the correct accounts when called with status disabled", %{
|
||||
conn: conn,
|
||||
disabled_account: %{id: disabled_account_id}
|
||||
} do
|
||||
assert [%{"id" => ^disabled_account_id}] =
|
||||
conn
|
||||
|> get("/api/v2/admin/accounts?status=disabled")
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
|
||||
test "returns the correct accounts when called with status pending", %{
|
||||
conn: conn,
|
||||
pending_account: %{id: pending_account_id}
|
||||
} do
|
||||
assert [%{"id" => ^pending_account_id}] =
|
||||
conn
|
||||
|> get("/api/v2/admin/accounts?status=pending")
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
|
||||
test "sets the correct pagination headers with limit param", %{
|
||||
conn: conn,
|
||||
admin_account: %{id: admin_account_id}
|
||||
} do
|
||||
response =
|
||||
conn
|
||||
|> get("/api/v2/admin/accounts?limit=1")
|
||||
|
||||
next_url =
|
||||
~r{<.+?(?<link>/api[^>]+)>; rel=\"next\"}
|
||||
|> Regex.named_captures(get_resp_header(response, "link") |> Enum.at(0))
|
||||
|> Map.get("link")
|
||||
|
||||
next_url =~ "&limit=1&max_id=#{admin_account_id}"
|
||||
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…
Reference in a new issue