added subject actor to moderation log

This commit is contained in:
Maksim Pechnikov 2020-11-19 22:13:45 +03:00
parent c8d11c3064
commit 66f411fba0
7 changed files with 240 additions and 68 deletions

View file

@ -194,6 +194,19 @@ def get_by_id(id) do
end end
end end
def get_by_id_with_user_actor(id) do
case FlakeId.flake_id?(id) do
true ->
Activity
|> where([a], a.id == ^id)
|> with_preloaded_user_actor()
|> Repo.one()
_ ->
nil
end
end
def get_by_id_with_object(id) do def get_by_id_with_object(id) do
Activity Activity
|> where(id: ^id) |> where(id: ^id)

View file

@ -112,16 +112,19 @@ def insert_log(%{
@spec insert_log(%{actor: User, subject: User, action: String.t()}) :: @spec insert_log(%{actor: User, subject: User, action: String.t()}) ::
{:ok, ModerationLog} | {:error, any} {:ok, ModerationLog} | {:error, any}
def insert_log(%{ def insert_log(
actor: %User{} = actor, %{
action: "report_update", actor: %User{} = actor,
subject: %Activity{data: %{"type" => "Flag"}} = subject action: "report_update",
}) do subject: %Activity{data: %{"type" => "Flag"}} = subject
} = attrs
) do
%ModerationLog{ %ModerationLog{
data: %{ data: %{
"actor" => user_to_map(actor), "actor" => user_to_map(actor),
"action" => "report_update", "action" => "report_update",
"subject" => report_to_map(subject), "subject" => report_to_map(subject),
"subject_actor" => user_to_map(attrs[:subject_actor]),
"message" => "" "message" => ""
} }
} }
@ -130,17 +133,20 @@ def insert_log(%{
@spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) :: @spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
{:ok, ModerationLog} | {:error, any} {:ok, ModerationLog} | {:error, any}
def insert_log(%{ def insert_log(
actor: %User{} = actor, %{
action: "report_note", actor: %User{} = actor,
subject: %Activity{} = subject, action: "report_note",
text: text subject: %Activity{} = subject,
}) do text: text
} = attrs
) do
%ModerationLog{ %ModerationLog{
data: %{ data: %{
"actor" => user_to_map(actor), "actor" => user_to_map(actor),
"action" => "report_note", "action" => "report_note",
"subject" => report_to_map(subject), "subject" => report_to_map(subject),
"subject_actor" => user_to_map(attrs[:subject_actor]),
"text" => text "text" => text
} }
} }
@ -149,17 +155,20 @@ def insert_log(%{
@spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) :: @spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
{:ok, ModerationLog} | {:error, any} {:ok, ModerationLog} | {:error, any}
def insert_log(%{ def insert_log(
actor: %User{} = actor, %{
action: "report_note_delete", actor: %User{} = actor,
subject: %Activity{} = subject, action: "report_note_delete",
text: text subject: %Activity{} = subject,
}) do text: text
} = attrs
) do
%ModerationLog{ %ModerationLog{
data: %{ data: %{
"actor" => user_to_map(actor), "actor" => user_to_map(actor),
"action" => "report_note_delete", "action" => "report_note_delete",
"subject" => report_to_map(subject), "subject" => report_to_map(subject),
"subject_actor" => user_to_map(attrs[:subject_actor]),
"text" => text "text" => text
} }
} }
@ -345,17 +354,18 @@ defp insert_log_entry_with_message(entry) do
end end
defp user_to_map(users) when is_list(users) do defp user_to_map(users) when is_list(users) do
users |> Enum.map(&user_to_map/1) Enum.map(users, &user_to_map/1)
end end
defp user_to_map(%User{} = user) do defp user_to_map(%User{} = user) do
user user
|> Map.from_struct()
|> Map.take([:id, :nickname]) |> Map.take([:id, :nickname])
|> Map.new(fn {k, v} -> {Atom.to_string(k), v} end) |> Map.new(fn {k, v} -> {Atom.to_string(k), v} end)
|> Map.put("type", "user") |> Map.put("type", "user")
end end
defp user_to_map(_), do: nil
defp report_to_map(%Activity{} = report) do defp report_to_map(%Activity{} = report) do
%{ %{
"type" => "report", "type" => "report",
@ -512,38 +522,48 @@ def get_log_entry_message(%ModerationLog{
end end
@spec get_log_entry_message(ModerationLog) :: String.t() @spec get_log_entry_message(ModerationLog) :: String.t()
def get_log_entry_message(%ModerationLog{ def get_log_entry_message(
data: %{ %ModerationLog{
"actor" => %{"nickname" => actor_nickname}, data: %{
"action" => "report_update", "actor" => %{"nickname" => actor_nickname},
"subject" => %{"id" => subject_id, "state" => state, "type" => "report"} "action" => "report_update",
} "subject" => %{"id" => subject_id, "state" => state, "type" => "report"}
}) do }
"@#{actor_nickname} updated report ##{subject_id} with '#{state}' state" } = log
) do
"@#{actor_nickname} updated report ##{subject_id}" <>
subject_actor_nickname(log, " (on user ", ")") <>
" with '#{state}' state"
end end
@spec get_log_entry_message(ModerationLog) :: String.t() @spec get_log_entry_message(ModerationLog) :: String.t()
def get_log_entry_message(%ModerationLog{ def get_log_entry_message(
data: %{ %ModerationLog{
"actor" => %{"nickname" => actor_nickname}, data: %{
"action" => "report_note", "actor" => %{"nickname" => actor_nickname},
"subject" => %{"id" => subject_id, "type" => "report"}, "action" => "report_note",
"text" => text "subject" => %{"id" => subject_id, "type" => "report"},
} "text" => text
}) do }
"@#{actor_nickname} added note '#{text}' to report ##{subject_id}" } = log
) do
"@#{actor_nickname} added note '#{text}' to report ##{subject_id}" <>
subject_actor_nickname(log, " on user ")
end end
@spec get_log_entry_message(ModerationLog) :: String.t() @spec get_log_entry_message(ModerationLog) :: String.t()
def get_log_entry_message(%ModerationLog{ def get_log_entry_message(
data: %{ %ModerationLog{
"actor" => %{"nickname" => actor_nickname}, data: %{
"action" => "report_note_delete", "actor" => %{"nickname" => actor_nickname},
"subject" => %{"id" => subject_id, "type" => "report"}, "action" => "report_note_delete",
"text" => text "subject" => %{"id" => subject_id, "type" => "report"},
} "text" => text
}) do }
"@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}" } = log
) do
"@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}" <>
subject_actor_nickname(log, " on user ")
end end
@spec get_log_entry_message(ModerationLog) :: String.t() @spec get_log_entry_message(ModerationLog) :: String.t()
@ -676,4 +696,16 @@ defp users_to_nicknames_string(users) do
|> Enum.map(&"@#{&1["nickname"]}") |> Enum.map(&"@#{&1["nickname"]}")
|> Enum.join(", ") |> Enum.join(", ")
end end
defp subject_actor_nickname(%ModerationLog{data: data}, prefix_msg, postfix_msg \\ "") do
case data do
%{"subject_actor" => %{"nickname" => subject_actor}} ->
[prefix_msg, "@#{subject_actor}", postfix_msg]
|> Enum.reject(&(&1 == ""))
|> Enum.join()
_ ->
""
end
end
end end

View file

@ -50,10 +50,13 @@ def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn,
Enum.map(reports, fn report -> Enum.map(reports, fn report ->
case CommonAPI.update_report_state(report.id, report.state) do case CommonAPI.update_report_state(report.id, report.state) do
{:ok, activity} -> {:ok, activity} ->
report = Activity.get_by_id_with_user_actor(activity.id)
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{
action: "report_update", action: "report_update",
actor: admin, actor: admin,
subject: activity subject: activity,
subject_actor: report.user_actor
}) })
activity activity
@ -73,11 +76,13 @@ def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn,
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{ def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
id: report_id id: report_id
}) do }) do
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do with {:ok, _} <- ReportNote.create(user.id, report_id, content),
report <- Activity.get_by_id_with_user_actor(report_id) do
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{
action: "report_note", action: "report_note",
actor: user, actor: user,
subject: Activity.get_by_id(report_id), subject: report,
subject_actor: report.user_actor,
text: content text: content
}) })
@ -91,11 +96,13 @@ def notes_delete(%{assigns: %{user: user}} = conn, %{
id: note_id, id: note_id,
report_id: report_id report_id: report_id
}) do }) do
with {:ok, note} <- ReportNote.destroy(note_id) do with {:ok, note} <- ReportNote.destroy(note_id),
report <- Activity.get_by_id_with_user_actor(report_id) do
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{
action: "report_note_delete", action: "report_note_delete",
actor: user, actor: user,
subject: Activity.get_by_id(report_id), subject: report,
subject_actor: report.user_actor,
text: note.content text: note.content
}) })

View file

@ -197,6 +197,13 @@ test "all_by_ids_with_object/1" do
assert [%{id: ^id1, object: %Object{}}, %{id: ^id2, object: %Object{}}] = activities assert [%{id: ^id1, object: %Object{}}, %{id: ^id2, object: %Object{}}] = activities
end end
test "get_by_id_with_user_actor/1" do
user = insert(:user)
activity = insert(:note_activity, note: insert(:note, user: user))
assert Activity.get_by_id_with_user_actor(activity.id).user_actor == user
end
test "get_by_id_with_object/1" do test "get_by_id_with_object/1" do
%{id: id} = insert(:note_activity) %{id: id} = insert(:note_activity)

View file

@ -186,7 +186,8 @@ test "logging report update", %{moderator: moderator} do
id: "9m9I1F4p8ftrTP6QTI", id: "9m9I1F4p8ftrTP6QTI",
data: %{ data: %{
"type" => "Flag", "type" => "Flag",
"state" => "resolved" "state" => "resolved",
"actor" => "http://localhost:4000/users/max"
} }
} }
@ -204,25 +205,37 @@ test "logging report update", %{moderator: moderator} do
end end
test "logging report response", %{moderator: moderator} do test "logging report response", %{moderator: moderator} do
user = insert(:user)
report = %Activity{ report = %Activity{
id: "9m9I1F4p8ftrTP6QTI", id: "9m9I1F4p8ftrTP6QTI",
data: %{ data: %{
"type" => "Note" "type" => "Note",
"actor" => user.ap_id
} }
} }
{:ok, _} = attrs = %{
ModerationLog.insert_log(%{ actor: moderator,
actor: moderator, action: "report_note",
action: "report_note", subject: report,
subject: report, text: "look at this"
text: "look at this" }
})
log = Repo.one(ModerationLog) {:ok, log1} = ModerationLog.insert_log(attrs)
log = Repo.get(ModerationLog, log1.id)
assert log.data["message"] == assert log.data["message"] ==
"@#{moderator.nickname} added note 'look at this' to report ##{report.id}" "@#{moderator.nickname} added note 'look at this' to report ##{report.id}"
{:ok, log2} = ModerationLog.insert_log(Map.merge(attrs, %{subject_actor: user}))
log = Repo.get(ModerationLog, log2.id)
assert log.data["message"] ==
"@#{moderator.nickname} added note 'look at this' to report ##{report.id} on user @#{
user.nickname
}"
end end
test "logging status sensitivity update", %{moderator: moderator} do test "logging status sensitivity update", %{moderator: moderator} do

View file

@ -122,13 +122,13 @@ test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
}) })
|> json_response_and_validate_schema(:no_content) |> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id(id) activity = Activity.get_by_id_with_user_actor(id)
assert activity.data["state"] == "resolved" assert activity.data["state"] == "resolved"
log_entry = Repo.one(ModerationLog) log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) == assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated report ##{id} with 'resolved' state" "@#{admin.nickname} updated report ##{id} (on user @#{activity.user_actor.nickname}) with 'resolved' state"
end end
test "closes report", %{conn: conn, id: id, admin: admin} do test "closes report", %{conn: conn, id: id, admin: admin} do
@ -141,13 +141,13 @@ test "closes report", %{conn: conn, id: id, admin: admin} do
}) })
|> json_response_and_validate_schema(:no_content) |> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id(id) activity = Activity.get_by_id_with_user_actor(id)
assert activity.data["state"] == "closed" assert activity.data["state"] == "closed"
log_entry = Repo.one(ModerationLog) log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) == assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated report ##{id} with 'closed' state" "@#{admin.nickname} updated report ##{id} (on user @#{activity.user_actor.nickname}) with 'closed' state"
end end
test "returns 400 when state is unknown", %{conn: conn, id: id} do test "returns 400 when state is unknown", %{conn: conn, id: id} do
@ -193,18 +193,20 @@ test "updates state of multiple reports", %{
}) })
|> json_response_and_validate_schema(:no_content) |> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id(id) activity = Activity.get_by_id_with_user_actor(id)
second_activity = Activity.get_by_id(second_report_id) second_activity = Activity.get_by_id_with_user_actor(second_report_id)
assert activity.data["state"] == "resolved" assert activity.data["state"] == "resolved"
assert second_activity.data["state"] == "closed" assert second_activity.data["state"] == "closed"
[first_log_entry, second_log_entry] = Repo.all(ModerationLog) [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
assert ModerationLog.get_log_entry_message(first_log_entry) == assert ModerationLog.get_log_entry_message(first_log_entry) ==
"@#{admin.nickname} updated report ##{id} with 'resolved' state" "@#{admin.nickname} updated report ##{id} (on user @#{activity.user_actor.nickname}) with 'resolved' state"
assert ModerationLog.get_log_entry_message(second_log_entry) == assert ModerationLog.get_log_entry_message(second_log_entry) ==
"@#{admin.nickname} updated report ##{second_report_id} with 'closed' state" "@#{admin.nickname} updated report ##{second_report_id} (on user @#{
second_activity.user_actor.nickname
}) with 'closed' state"
end end
end end

View file

@ -0,0 +1,98 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ModerationLogViewTest do
use Pleroma.DataCase
alias Pleroma.Web.AdminAPI.ModerationLogView
describe "renders `report_note_delete` log messages" do
setup do
log1 = %Pleroma.ModerationLog{
data: %{
"action" => "report_note_delete",
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
"message" => "@admin deleted note 'mistake' from report #A1I7be on user @b-612",
"subject" => %{"id" => "A1I7be", "state" => "open", "type" => "report"},
"subject_actor" => %{"id" => "A1I7G8", "nickname" => "b-612", "type" => "user"},
"text" => "mistake"
},
inserted_at: ~N[2020-11-17 14:13:20]
}
log2 = %Pleroma.ModerationLog{
data: %{
"action" => "report_note_delete",
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
"message" => "@admin deleted note 'fake user' from report #A1I7be on user @j-612",
"subject" => %{"id" => "A1I7be", "state" => "open", "type" => "report"},
"subject_actor" => %{"id" => "A1I7G8", "nickname" => "j-612", "type" => "user"},
"text" => "fake user"
},
inserted_at: ~N[2020-11-17 14:13:20]
}
{:ok, %{log1: log1, log2: log2}}
end
test "renders `report_note_delete` log messages", %{log1: log1, log2: log2} do
assert ModerationLogView.render(
"index.json",
%{log: %{items: [log1, log2], count: 2}}
) == %{
items: [
%{
data: %{
"action" => "report_note_delete",
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
"message" =>
"@admin deleted note 'mistake' from report #A1I7be on user @b-612",
"subject" => %{"id" => "A1I7be", "state" => "open", "type" => "report"},
"subject_actor" => %{
"id" => "A1I7G8",
"nickname" => "b-612",
"type" => "user"
},
"text" => "mistake"
},
message: "@admin deleted note 'mistake' from report #A1I7be on user @b-612",
time: 1_605_622_400
},
%{
data: %{
"action" => "report_note_delete",
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
"message" =>
"@admin deleted note 'fake user' from report #A1I7be on user @j-612",
"subject" => %{"id" => "A1I7be", "state" => "open", "type" => "report"},
"subject_actor" => %{
"id" => "A1I7G8",
"nickname" => "j-612",
"type" => "user"
},
"text" => "fake user"
},
message: "@admin deleted note 'fake user' from report #A1I7be on user @j-612",
time: 1_605_622_400
}
],
total: 2
}
end
test "renders `report_note_delete` log message", %{log1: log} do
assert ModerationLogView.render("show.json", %{log_entry: log}) == %{
data: %{
"action" => "report_note_delete",
"actor" => %{"id" => "A1I7G8", "nickname" => "admin", "type" => "user"},
"message" => "@admin deleted note 'mistake' from report #A1I7be on user @b-612",
"subject" => %{"id" => "A1I7be", "state" => "open", "type" => "report"},
"subject_actor" => %{"id" => "A1I7G8", "nickname" => "b-612", "type" => "user"},
"text" => "mistake"
},
message: "@admin deleted note 'mistake' from report #A1I7be on user @b-612",
time: 1_605_622_400
}
end
end
end