Browse Source

Merge branch 'feature/admin-api-reports' into 'develop'

Add Reports to Admin API

Closes #774

See merge request pleroma/pleroma!1107
tags/v1.1.4
feld 5 years ago
parent
commit
0f8892686a
16 changed files with 945 additions and 20 deletions
  1. +2
    -0
      CHANGELOG.md
  2. +283
    -6
      docs/api/admin_api.md
  3. +1
    -1
      lib/pleroma/activity.ex
  4. +1
    -1
      lib/pleroma/emails/admin_email.ex
  5. +7
    -0
      lib/pleroma/web/activity_pub/activity_pub.ex
  6. +77
    -1
      lib/pleroma/web/activity_pub/utils.ex
  7. +81
    -0
      lib/pleroma/web/admin_api/admin_api_controller.ex
  8. +41
    -0
      lib/pleroma/web/admin_api/views/report_view.ex
  9. +57
    -0
      lib/pleroma/web/common_api/common_api.ex
  10. +4
    -6
      lib/pleroma/web/common_api/utils.ex
  11. +8
    -0
      lib/pleroma/web/router.ex
  12. +19
    -0
      priv/repo/migrations/20190511191044_set_default_state_to_reports.exs
  13. +3
    -2
      test/support/factory.ex
  14. +327
    -0
      test/web/admin_api/admin_api_controller_test.exs
  15. +32
    -1
      test/web/common_api/common_api_test.exs
  16. +2
    -2
      test/web/mastodon_api/mastodon_api_controller_test.exs

+ 2
- 0
CHANGELOG.md View File

@@ -24,6 +24,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Admin API: Endpoints for listing/revoking invite tokens
- Admin API: Endpoints for making users follow/unfollow each other
- Admin API: added filters (role, tags, email, name) for users endpoint
- Admin API: Endpoints for managing reports
- Admin API: Endpoints for deleting and changing the scope of individual reported statuses
- AdminFE: initial release with basic user management accessible at /pleroma/admin/
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)


+ 283
- 6
docs/api/admin_api.md View File

@@ -24,7 +24,7 @@ Authentication is required and the user must be an admin.
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
- Response:

```JSON
```json
{
"page_size": integer,
"count": integer,
@@ -92,7 +92,7 @@ Authentication is required and the user must be an admin.
- `nickname`
- Response: User’s object

```JSON
```json
{
"deactivated": bool,
"id": integer,
@@ -124,7 +124,7 @@ Authentication is required and the user must be an admin.
- Params: none
- Response:

```JSON
```json
{
"is_moderator": bool,
"is_admin": bool
@@ -141,7 +141,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- Params: none
- Response:

```JSON
```json
{
"is_moderator": bool,
"is_admin": bool
@@ -223,7 +223,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- Params: none
- Response:

```JSON
```json
{

"invites": [
@@ -250,7 +250,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `token`
- Response:

```JSON
```json
{
"id": integer,
"token": string,
@@ -280,3 +280,280 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- Methods: `GET`
- Params: none
- Response: password reset token (base64 string)

## `/api/pleroma/admin/reports`
### Get a list of reports
- Method `GET`
- Params:
- `state`: optional, the state of reports. Valid values are `open`, `closed` and `resolved`
- `limit`: optional, the number of records to retrieve
- `since_id`: optional, returns results that are more recent than the specified id
- `max_id`: optional, returns results that are older than the specified id
- Response:
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
- On success: JSON, returns a list of reports, where:
- `account`: the user who has been reported
- `actor`: the user who has sent the report
- `statuses`: list of statuses that have been included to the report

```json
{
"reports": [
{
"account": {
"acct": "user",
"avatar": "https://pleroma.example.org/images/avi.png",
"avatar_static": "https://pleroma.example.org/images/avi.png",
"bot": false,
"created_at": "2019-04-23T17:32:04.000Z",
"display_name": "User",
"emojis": [],
"fields": [],
"followers_count": 1,
"following_count": 1,
"header": "https://pleroma.example.org/images/banner.png",
"header_static": "https://pleroma.example.org/images/banner.png",
"id": "9i6dAJqSGSKMzLG2Lo",
"locked": false,
"note": "",
"pleroma": {
"confirmation_pending": false,
"hide_favorites": true,
"hide_followers": false,
"hide_follows": false,
"is_admin": false,
"is_moderator": false,
"relationship": {},
"tags": []
},
"source": {
"note": "",
"pleroma": {},
"sensitive": false
},
"statuses_count": 3,
"url": "https://pleroma.example.org/users/user",
"username": "user"
},
"actor": {
"acct": "lain",
"avatar": "https://pleroma.example.org/images/avi.png",
"avatar_static": "https://pleroma.example.org/images/avi.png",
"bot": false,
"created_at": "2019-03-28T17:36:03.000Z",
"display_name": "Roger Braun",
"emojis": [],
"fields": [],
"followers_count": 1,
"following_count": 1,
"header": "https://pleroma.example.org/images/banner.png",
"header_static": "https://pleroma.example.org/images/banner.png",
"id": "9hEkA5JsvAdlSrocam",
"locked": false,
"note": "",
"pleroma": {
"confirmation_pending": false,
"hide_favorites": false,
"hide_followers": false,
"hide_follows": false,
"is_admin": false,
"is_moderator": false,
"relationship": {},
"tags": []
},
"source": {
"note": "",
"pleroma": {},
"sensitive": false
},
"statuses_count": 1,
"url": "https://pleroma.example.org/users/lain",
"username": "lain"
},
"content": "Please delete it",
"created_at": "2019-04-29T19:48:15.000Z",
"id": "9iJGOv1j8hxuw19bcm",
"state": "open",
"statuses": [
{
"account": { ... },
"application": {
"name": "Web",
"website": null
},
"bookmarked": false,
"card": null,
"content": "<span class=\"h-card\"><a data-user=\"9hEkA5JsvAdlSrocam\" class=\"u-url mention\" href=\"https://pleroma.example.org/users/lain\">@<span>lain</span></a></span> click on my link <a href=\"https://www.google.com/\">https://www.google.com/</a>",
"created_at": "2019-04-23T19:15:47.000Z",
"emojis": [],
"favourited": false,
"favourites_count": 0,
"id": "9i6mQ9uVrrOmOime8m",
"in_reply_to_account_id": null,
"in_reply_to_id": null,
"language": null,
"media_attachments": [],
"mentions": [
{
"acct": "lain",
"id": "9hEkA5JsvAdlSrocam",
"url": "https://pleroma.example.org/users/lain",
"username": "lain"
},
{
"acct": "user",
"id": "9i6dAJqSGSKMzLG2Lo",
"url": "https://pleroma.example.org/users/user",
"username": "user"
}
],
"muted": false,
"pinned": false,
"pleroma": {
"content": {
"text/plain": "@lain click on my link https://www.google.com/"
},
"conversation_id": 28,
"in_reply_to_account_acct": null,
"local": true,
"spoiler_text": {
"text/plain": ""
}
},
"reblog": null,
"reblogged": false,
"reblogs_count": 0,
"replies_count": 0,
"sensitive": false,
"spoiler_text": "",
"tags": [],
"uri": "https://pleroma.example.org/objects/8717b90f-8e09-4b58-97b0-e3305472b396",
"url": "https://pleroma.example.org/notice/9i6mQ9uVrrOmOime8m",
"visibility": "direct"
}
]
}
]
}
```

## `/api/pleroma/admin/reports/:id`
### Get an individual report
- Method `GET`
- Params:
- `id`
- Response:
- On failure:
- 403 Forbidden `{"error": "error_msg"}`
- 404 Not Found `"Not found"`
- On success: JSON, Report object (see above)

## `/api/pleroma/admin/reports/:id`
### Change the state of the report
- Method `PUT`
- Params:
- `id`
- `state`: required, the new state. Valid values are `open`, `closed` and `resolved`
- Response:
- On failure:
- 400 Bad Request `"Unsupported state"`
- 403 Forbidden `{"error": "error_msg"}`
- 404 Not Found `"Not found"`
- On success: JSON, Report object (see above)

## `/api/pleroma/admin/reports/:id/respond`
### Respond to a report
- Method `POST`
- Params:
- `id`
- `status`: required, the message
- Response:
- On failure:
- 400 Bad Request `"Invalid parameters"` when `status` is missing
- 403 Forbidden `{"error": "error_msg"}`
- 404 Not Found `"Not found"`
- On success: JSON, created Mastodon Status entity

```json
{
"account": { ... },
"application": {
"name": "Web",
"website": null
},
"bookmarked": false,
"card": null,
"content": "Your claim is going to be closed",
"created_at": "2019-05-11T17:13:03.000Z",
"emojis": [],
"favourited": false,
"favourites_count": 0,
"id": "9ihuiSL1405I65TmEq",
"in_reply_to_account_id": null,
"in_reply_to_id": null,
"language": null,
"media_attachments": [],
"mentions": [
{
"acct": "user",
"id": "9i6dAJqSGSKMzLG2Lo",
"url": "https://pleroma.example.org/users/user",
"username": "user"
},
{
"acct": "admin",
"id": "9hEkA5JsvAdlSrocam",
"url": "https://pleroma.example.org/users/admin",
"username": "admin"
}
],
"muted": false,
"pinned": false,
"pleroma": {
"content": {
"text/plain": "Your claim is going to be closed"
},
"conversation_id": 35,
"in_reply_to_account_acct": null,
"local": true,
"spoiler_text": {
"text/plain": ""
}
},
"reblog": null,
"reblogged": false,
"reblogs_count": 0,
"replies_count": 0,
"sensitive": false,
"spoiler_text": "",
"tags": [],
"uri": "https://pleroma.example.org/objects/cab0836d-9814-46cd-a0ea-529da9db5fcb",
"url": "https://pleroma.example.org/notice/9ihuiSL1405I65TmEq",
"visibility": "direct"
}
```

## `/api/pleroma/admin/statuses/:id`
### Change the scope of an individual reported status
- Method `PUT`
- Params:
- `id`
- `sensitive`: optional, valid values are `true` or `false`
- `visibility`: optional, valid values are `public`, `private` and `unlisted`
- Response:
- On failure:
- 400 Bad Request `"Unsupported visibility"`
- 403 Forbidden `{"error": "error_msg"}`
- 404 Not Found `"Not found"`
- On success: JSON, Mastodon Status entity

## `/api/pleroma/admin/statuses/:id`
### Delete an individual reported status
- Method `DELETE`
- Params:
- `id`
- Response:
- On failure:
- 403 Forbidden `{"error": "error_msg"}`
- 404 Not Found `"Not found"`
- On success: 200 OK `{}`

+ 1
- 1
lib/pleroma/activity.ex View File

@@ -111,7 +111,7 @@ defmodule Pleroma.Activity do

def change(struct, params \\ %{}) do
struct
|> cast(params, [:data])
|> cast(params, [:data, :recipients])
|> validate_required([:data])
|> unique_constraint(:ap_id, name: :activities_unique_apid_index)
end


+ 1
- 1
lib/pleroma/emails/admin_email.ex View File

@@ -29,7 +29,7 @@ defmodule Pleroma.Emails.AdminEmail do
end

statuses_html =
if length(statuses) > 0 do
if is_list(statuses) && length(statuses) > 0 do
statuses_list_html =
statuses
|> Enum.map(fn


+ 7
- 0
lib/pleroma/web/activity_pub/activity_pub.ex View File

@@ -703,6 +703,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do

defp restrict_type(query, _), do: query

defp restrict_state(query, %{"state" => state}) do
from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
end

defp restrict_state(query, _), do: query

defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
from(
activity in query,
@@ -855,6 +861,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_local(opts)
|> restrict_actor(opts)
|> restrict_type(opts)
|> restrict_state(opts)
|> restrict_favorited_by(opts)
|> restrict_blocked(opts)
|> restrict_muted(opts)


+ 77
- 1
lib/pleroma/web/activity_pub/utils.ex View File

@@ -20,6 +20,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
require Logger

@supported_object_types ["Article", "Note", "Video", "Page"]
@supported_report_states ~w(open closed resolved)
@valid_visibilities ~w(public unlisted private direct)

# Some implementations send the actor URI as the actor field, others send the entire actor object,
# so figure out what the actor's URI is based on what we have.
@@ -670,7 +672,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"actor" => params.actor.ap_id,
"content" => params.content,
"object" => object,
"context" => params.context
"context" => params.context,
"state" => "open"
}
|> Map.merge(additional)
end
@@ -713,4 +716,77 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
end
end

#### Report-related helpers

def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
with new_data <- Map.put(activity.data, "state", state),
changeset <- Changeset.change(activity, data: new_data),
{:ok, activity} <- Repo.update(changeset) do
{:ok, activity}
end
end

def update_report_state(_, _), do: {:error, "Unsupported state"}

def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do
[to, cc, recipients] =
activity
|> get_updated_targets(visibility)
|> Enum.map(&Enum.uniq/1)

object_data =
activity.object.data
|> Map.put("to", to)
|> Map.put("cc", cc)

{:ok, object} =
activity.object
|> Object.change(%{data: object_data})
|> Object.update_and_set_cache()

activity_data =
activity.data
|> Map.put("to", to)
|> Map.put("cc", cc)

activity
|> Map.put(:object, object)
|> Activity.change(%{data: activity_data, recipients: recipients})
|> Repo.update()
end

def update_activity_visibility(_, _), do: {:error, "Unsupported visibility"}

defp get_updated_targets(
%Activity{data: %{"to" => to} = data, recipients: recipients},
visibility
) do
cc = Map.get(data, "cc", [])
follower_address = User.get_cached_by_ap_id(data["actor"]).follower_address
public = "https://www.w3.org/ns/activitystreams#Public"

case visibility do
"public" ->
to = [public | List.delete(to, follower_address)]
cc = [follower_address | List.delete(cc, public)]
recipients = [public | recipients]
[to, cc, recipients]

"private" ->
to = [follower_address | List.delete(to, public)]
cc = List.delete(cc, public)
recipients = List.delete(recipients, public)
[to, cc, recipients]

"unlisted" ->
to = [follower_address | List.delete(to, public)]
cc = [public | List.delete(cc, follower_address)]
recipients = recipients ++ [follower_address, public]
[to, cc, recipients]

_ ->
[to, cc, recipients]
end
end
end

+ 81
- 0
lib/pleroma/web/admin_api/admin_api_controller.ex View File

@@ -4,11 +4,16 @@

defmodule Pleroma.Web.AdminAPI.AdminAPIController do
use Pleroma.Web, :controller
alias Pleroma.Activity
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.AdminAPI.ReportView
alias Pleroma.Web.AdminAPI.Search
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.StatusView

import Pleroma.Web.ControllerHelper, only: [json_response: 3]

@@ -287,12 +292,88 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(token.token)
end

def list_reports(conn, params) do
params =
params
|> Map.put("type", "Flag")
|> Map.put("skip_preload", true)

reports =
[]
|> ActivityPub.fetch_activities(params)
|> Enum.reverse()

conn
|> put_view(ReportView)
|> render("index.json", %{reports: reports})
end

def report_show(conn, %{"id" => id}) do
with %Activity{} = report <- Activity.get_by_id(id) do
conn
|> put_view(ReportView)
|> render("show.json", %{report: report})
else
_ -> {:error, :not_found}
end
end

def report_update_state(conn, %{"id" => id, "state" => state}) do
with {:ok, report} <- CommonAPI.update_report_state(id, state) do
conn
|> put_view(ReportView)
|> render("show.json", %{report: report})
end
end

def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
with false <- is_nil(params["status"]),
%Activity{} <- Activity.get_by_id(id) do
params =
params
|> Map.put("in_reply_to_status_id", id)
|> Map.put("visibility", "direct")

{:ok, activity} = CommonAPI.post(user, params)

conn
|> put_view(StatusView)
|> render("status.json", %{activity: activity})
else
true ->
{:param_cast, nil}

nil ->
{:error, :not_found}
end
end

def status_update(conn, %{"id" => id} = params) do
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
conn
|> put_view(StatusView)
|> render("status.json", %{activity: activity})
end
end

def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
json(conn, %{})
end
end

def errors(conn, {:error, :not_found}) do
conn
|> put_status(404)
|> json("Not found")
end

def errors(conn, {:error, reason}) do
conn
|> put_status(400)
|> json(reason)
end

def errors(conn, {:param_cast, _}) do
conn
|> put_status(400)


+ 41
- 0
lib/pleroma/web/admin_api/views/report_view.ex View File

@@ -0,0 +1,41 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.AdminAPI.ReportView do
use Pleroma.Web, :view
alias Pleroma.Activity
alias Pleroma.User
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView

def render("index.json", %{reports: reports}) do
%{
reports: render_many(reports, __MODULE__, "show.json", as: :report)
}
end

def render("show.json", %{report: report}) do
user = User.get_cached_by_ap_id(report.data["actor"])
created_at = Utils.to_masto_date(report.data["published"])

[account_ap_id | status_ap_ids] = report.data["object"]
account = User.get_cached_by_ap_id(account_ap_id)

statuses =
Enum.map(status_ap_ids, fn ap_id ->
Activity.get_by_ap_id_with_object(ap_id)
end)

%{
id: report.id,
account: AccountView.render("account.json", %{user: account}),
actor: AccountView.render("account.json", %{user: user}),
content: report.data["content"],
created_at: created_at,
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
state: report.data["state"]
}
end
end

+ 57
- 0
lib/pleroma/web/common_api/common_api.ex View File

@@ -71,6 +71,9 @@ defmodule Pleroma.Web.CommonAPI do
{:ok, _} <- unpin(activity_id, user),
{:ok, delete} <- ActivityPub.delete(object) do
{:ok, delete}
else
_ ->
{:error, "Could not delete"}
end
end

@@ -315,6 +318,60 @@ defmodule Pleroma.Web.CommonAPI do
end
end

def update_report_state(activity_id, state) do
with %Activity{} = activity <- Activity.get_by_id(activity_id),
{:ok, activity} <- Utils.update_report_state(activity, state) do
{:ok, activity}
else
nil ->
{:error, :not_found}

{:error, reason} ->
{:error, reason}

_ ->
{:error, "Could not update state"}
end
end

def update_activity_scope(activity_id, opts \\ %{}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
{:ok, activity} <- toggle_sensitive(activity, opts),
{:ok, activity} <- set_visibility(activity, opts) do
{:ok, activity}
else
nil ->
{:error, :not_found}

{:error, reason} ->
{:error, reason}
end
end

defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do
toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)})
end

defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive})
when is_boolean(sensitive) do
new_data = Map.put(object.data, "sensitive", sensitive)

{:ok, object} =
object
|> Object.change(%{data: new_data})
|> Object.update_and_set_cache()

{:ok, Map.put(activity, :object, object)}
end

defp toggle_sensitive(activity, _), do: {:ok, activity}

defp set_visibility(activity, %{"visibility" => visibility}) do
Utils.update_activity_visibility(activity, visibility)
end

defp set_visibility(activity, _), do: {:ok, activity}

def hide_reblogs(user, muted) do
ap_id = muted.ap_id



+ 4
- 6
lib/pleroma/web/common_api/utils.ex View File

@@ -237,13 +237,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
}

if in_reply_to do
in_reply_to_object = Object.normalize(in_reply_to)

object
|> Map.put("inReplyTo", in_reply_to_object.data["id"])
with false <- is_nil(in_reply_to),
%Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
else
object
_ -> object
end
end



+ 8
- 0
lib/pleroma/web/router.ex View File

@@ -194,6 +194,14 @@ defmodule Pleroma.Web.Router do

get("/users", AdminAPIController, :list_users)
get("/users/:nickname", AdminAPIController, :user_show)

get("/reports", AdminAPIController, :list_reports)
get("/reports/:id", AdminAPIController, :report_show)
put("/reports/:id", AdminAPIController, :report_update_state)
post("/reports/:id/respond", AdminAPIController, :report_respond)

put("/statuses/:id", AdminAPIController, :status_update)
delete("/statuses/:id", AdminAPIController, :status_delete)
end

scope "/", Pleroma.Web.TwitterAPI do


+ 19
- 0
priv/repo/migrations/20190511191044_set_default_state_to_reports.exs View File

@@ -0,0 +1,19 @@
defmodule Pleroma.Repo.Migrations.SetDefaultStateToReports do
use Ecto.Migration

def up do
execute """
UPDATE activities AS a
SET data = jsonb_set(data, '{state}', '"open"', true)
WHERE data->>'type' = 'Flag'
"""
end

def down do
execute """
UPDATE activities AS a
SET data = data #- '{state}'
WHERE data->>'type' = 'Flag'
"""
end
end

+ 3
- 2
test/support/factory.ex View File

@@ -43,7 +43,7 @@ defmodule Pleroma.Factory do
def note_factory(attrs \\ %{}) do
text = sequence(:text, &"This is :moominmamma: note #{&1}")

user = insert(:user)
user = attrs[:user] || insert(:user)

data = %{
"type" => "Note",
@@ -113,7 +113,8 @@ defmodule Pleroma.Factory do
end

def note_activity_factory(attrs \\ %{}) do
note = attrs[:note] || insert(:note)
user = attrs[:user] || insert(:user)
note = attrs[:note] || insert(:note, user: user)

data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),


+ 327
- 0
test/web/admin_api/admin_api_controller_test.exs View File

@@ -5,8 +5,10 @@
defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
use Pleroma.Web.ConnCase

alias Pleroma.Activity
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.CommonAPI
import Pleroma.Factory

describe "/api/pleroma/admin/users" do
@@ -949,4 +951,329 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
}
end
end

describe "GET /api/pleroma/admin/reports/:id" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})

%{conn: assign(conn, :user, admin)}
end

test "returns report by its id", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %{id: report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

response =
conn
|> get("/api/pleroma/admin/reports/#{report_id}")
|> json_response(:ok)

assert response["id"] == report_id
end

test "returns 404 when report id is invalid", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/reports/test")

assert json_response(conn, :not_found) == "Not found"
end
end

describe "PUT /api/pleroma/admin/reports/:id" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %{id: report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

%{conn: assign(conn, :user, admin), id: report_id}
end

test "mark report as resolved", %{conn: conn, id: id} do
response =
conn
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"})
|> json_response(:ok)

assert response["state"] == "resolved"
end

test "closes report", %{conn: conn, id: id} do
response =
conn
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"})
|> json_response(:ok)

assert response["state"] == "closed"
end

test "returns 400 when state is unknown", %{conn: conn, id: id} do
conn =
conn
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "test"})

assert json_response(conn, :bad_request) == "Unsupported state"
end

test "returns 404 when report is not exist", %{conn: conn} do
conn =
conn
|> put("/api/pleroma/admin/reports/test", %{"state" => "closed"})

assert json_response(conn, :not_found) == "Not found"
end
end

describe "GET /api/pleroma/admin/reports" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})

%{conn: assign(conn, :user, admin)}
end

test "returns empty response when no reports created", %{conn: conn} do
response =
conn
|> get("/api/pleroma/admin/reports")
|> json_response(:ok)

assert Enum.empty?(response["reports"])
end

test "returns reports", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %{id: report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

response =
conn
|> get("/api/pleroma/admin/reports")
|> json_response(:ok)

[report] = response["reports"]

assert length(response["reports"]) == 1
assert report["id"] == report_id
end

test "returns reports with specified state", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %{id: first_report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

{:ok, %{id: second_report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I don't like this user"
})

CommonAPI.update_report_state(second_report_id, "closed")

response =
conn
|> get("/api/pleroma/admin/reports", %{
"state" => "open"
})
|> json_response(:ok)

[open_report] = response["reports"]

assert length(response["reports"]) == 1
assert open_report["id"] == first_report_id

response =
conn
|> get("/api/pleroma/admin/reports", %{
"state" => "closed"
})
|> json_response(:ok)

[closed_report] = response["reports"]

assert length(response["reports"]) == 1
assert closed_report["id"] == second_report_id

response =
conn
|> get("/api/pleroma/admin/reports", %{
"state" => "resolved"
})
|> json_response(:ok)

assert Enum.empty?(response["reports"])
end

test "returns 403 when requested by a non-admin" do
user = insert(:user)

conn =
build_conn()
|> assign(:user, user)
|> get("/api/pleroma/admin/reports")

assert json_response(conn, :forbidden) == %{"error" => "User is not admin."}
end

test "returns 403 when requested by anonymous" do
conn =
build_conn()
|> get("/api/pleroma/admin/reports")

assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
end
end

describe "POST /api/pleroma/admin/reports/:id/respond" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})

%{conn: assign(conn, :user, admin)}
end

test "returns created dm", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %{id: report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

response =
conn
|> post("/api/pleroma/admin/reports/#{report_id}/respond", %{
"status" => "I will check it out"
})
|> json_response(:ok)

recipients = Enum.map(response["mentions"], & &1["username"])

assert conn.assigns[:user].nickname in recipients
assert reporter.nickname in recipients
assert response["content"] == "I will check it out"
assert response["visibility"] == "direct"
end

test "returns 400 when status is missing", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/reports/test/respond")

assert json_response(conn, :bad_request) == "Invalid parameters"
end

test "returns 404 when report id is invalid", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/reports/test/respond", %{
"status" => "foo"
})

assert json_response(conn, :not_found) == "Not found"
end
end

describe "PUT /api/pleroma/admin/statuses/:id" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})
activity = insert(:note_activity)

%{conn: assign(conn, :user, admin), id: activity.id}
end

test "toggle sensitive flag", %{conn: conn, id: id} do
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|> json_response(:ok)

assert response["sensitive"]

response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|> json_response(:ok)

refute response["sensitive"]
end

test "change visibility flag", %{conn: conn, id: id} do
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
|> json_response(:ok)

assert response["visibility"] == "public"

response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
|> json_response(:ok)

assert response["visibility"] == "private"

response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
|> json_response(:ok)

assert response["visibility"] == "unlisted"
end

test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
conn =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})

assert json_response(conn, :bad_request) == "Unsupported visibility"
end
end

describe "DELETE /api/pleroma/admin/statuses/:id" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})
activity = insert(:note_activity)

%{conn: assign(conn, :user, admin), id: activity.id}
end

test "deletes status", %{conn: conn, id: id} do
conn
|> delete("/api/pleroma/admin/statuses/#{id}")
|> json_response(:ok)

refute Activity.get_by_id(id)
end

test "returns error when status is not exist", %{conn: conn} do
conn =
conn
|> delete("/api/pleroma/admin/statuses/test")

assert json_response(conn, :bad_request) == "Could not delete"
end
end
end

+ 32
- 1
test/web/common_api/common_api_test.exs View File

@@ -261,10 +261,41 @@ defmodule Pleroma.Web.CommonAPITest do
data: %{
"type" => "Flag",
"content" => ^comment,
"object" => [^target_ap_id, ^activity_ap_id]
"object" => [^target_ap_id, ^activity_ap_id],
"state" => "open"
}
} = flag_activity
end

test "updates report state" do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %Activity{id: report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

{:ok, report} = CommonAPI.update_report_state(report_id, "resolved")

assert report.data["state"] == "resolved"
end

test "does not update report state when state is unsupported" do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)

{:ok, %Activity{id: report_id}} =
CommonAPI.report(reporter, %{
"account_id" => target_user.id,
"comment" => "I feel offended",
"status_ids" => [activity.id]
})

assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
end
end

describe "reblog muting" do


+ 2
- 2
test/web/mastodon_api/mastodon_api_controller_test.exs View File

@@ -2129,7 +2129,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|> json_response(:ok)

assert length(anonymous_response) == 0
assert Enum.empty?(anonymous_response)
end

test "does not return others' favorited DM when user is not one of recipients", %{
@@ -2153,7 +2153,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|> json_response(:ok)

assert length(response) == 0
assert Enum.empty?(response)
end

test "paginates favorites using since_id and max_id", %{


Loading…
Cancel
Save