Log admin/moderator actions

This commit is contained in:
Maxim Filippov 2019-08-25 19:39:37 +00:00 committed by kaniini
commit 37dd3867bb
11 changed files with 1187 additions and 41 deletions

View file

@ -0,0 +1,301 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ModerationLogTest do
alias Pleroma.Activity
alias Pleroma.ModerationLog
use Pleroma.DataCase
import Pleroma.Factory
describe "user moderation" do
setup do
admin = insert(:user, info: %{is_admin: true})
moderator = insert(:user, info: %{is_moderator: true})
subject1 = insert(:user)
subject2 = insert(:user)
[admin: admin, moderator: moderator, subject1: subject1, subject2: subject2]
end
test "logging user deletion by moderator", %{moderator: moderator, subject1: subject1} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
subject: subject1,
action: "delete"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} deleted user @#{subject1.nickname}"
end
test "logging user creation by moderator", %{
moderator: moderator,
subject1: subject1,
subject2: subject2
} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
subjects: [subject1, subject2],
action: "create"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} created users: @#{subject1.nickname}, @#{subject2.nickname}"
end
test "logging user follow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: admin,
followed: subject1,
follower: subject2,
action: "follow"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{admin.nickname} made @#{subject2.nickname} follow @#{subject1.nickname}"
end
test "logging user unfollow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: admin,
followed: subject1,
follower: subject2,
action: "unfollow"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{admin.nickname} made @#{subject2.nickname} unfollow @#{subject1.nickname}"
end
test "logging user tagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: admin,
nicknames: [subject1.nickname, subject2.nickname],
tags: ["foo", "bar"],
action: "tag"
})
log = Repo.one(ModerationLog)
users =
[subject1.nickname, subject2.nickname]
|> Enum.map(&"@#{&1}")
|> Enum.join(", ")
tags = ["foo", "bar"] |> Enum.join(", ")
assert ModerationLog.get_log_entry_message(log) ==
"@#{admin.nickname} added tags: #{tags} to users: #{users}"
end
test "logging user untagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: admin,
nicknames: [subject1.nickname, subject2.nickname],
tags: ["foo", "bar"],
action: "untag"
})
log = Repo.one(ModerationLog)
users =
[subject1.nickname, subject2.nickname]
|> Enum.map(&"@#{&1}")
|> Enum.join(", ")
tags = ["foo", "bar"] |> Enum.join(", ")
assert ModerationLog.get_log_entry_message(log) ==
"@#{admin.nickname} removed tags: #{tags} from users: #{users}"
end
test "logging user grant by moderator", %{moderator: moderator, subject1: subject1} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
subject: subject1,
action: "grant",
permission: "moderator"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} made @#{subject1.nickname} moderator"
end
test "logging user revoke by moderator", %{moderator: moderator, subject1: subject1} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
subject: subject1,
action: "revoke",
permission: "moderator"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} revoked moderator role from @#{subject1.nickname}"
end
test "logging relay follow", %{moderator: moderator} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "relay_follow",
target: "https://example.org/relay"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} followed relay: https://example.org/relay"
end
test "logging relay unfollow", %{moderator: moderator} do
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "relay_unfollow",
target: "https://example.org/relay"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} unfollowed relay: https://example.org/relay"
end
test "logging report update", %{moderator: moderator} do
report = %Activity{
id: "9m9I1F4p8ftrTP6QTI",
data: %{
"type" => "Flag",
"state" => "resolved"
}
}
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "report_update",
subject: report
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} updated report ##{report.id} with 'resolved' state"
end
test "logging report response", %{moderator: moderator} do
report = %Activity{
id: "9m9I1F4p8ftrTP6QTI",
data: %{
"type" => "Note"
}
}
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "report_response",
subject: report,
text: "look at this"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} responded with 'look at this' to report ##{report.id}"
end
test "logging status sensitivity update", %{moderator: moderator} do
note = insert(:note_activity)
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "status_update",
subject: note,
sensitive: "true",
visibility: nil
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true'"
end
test "logging status visibility update", %{moderator: moderator} do
note = insert(:note_activity)
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "status_update",
subject: note,
sensitive: nil,
visibility: "private"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} updated status ##{note.id}, set visibility: 'private'"
end
test "logging status sensitivity & visibility update", %{moderator: moderator} do
note = insert(:note_activity)
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "status_update",
subject: note,
sensitive: "true",
visibility: "private"
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true', visibility: 'private'"
end
test "logging status deletion", %{moderator: moderator} do
note = insert(:note_activity)
{:ok, _} =
ModerationLog.insert_log(%{
actor: moderator,
action: "status_delete",
subject_id: note.id
})
log = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log) ==
"@#{moderator.nickname} deleted status ##{note.id}"
end
end
end

View file

@ -7,6 +7,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
alias Pleroma.Activity
alias Pleroma.HTML
alias Pleroma.ModerationLog
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.CommonAPI
@ -24,6 +26,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|> put_req_header("accept", "application/json")
|> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
log_entry = Repo.one(ModerationLog)
assert log_entry.data["subject"]["nickname"] == user.nickname
assert log_entry.data["action"] == "delete"
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deleted user @#{user.nickname}"
assert json_response(conn, 200) == user.nickname
end
@ -51,6 +61,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
assert response == ["success", "success"]
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} created users: @lain2, @lain"
end
test "Cannot create user with exisiting email" do
@ -218,6 +233,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
follower = User.get_cached_by_id(follower.id)
assert User.following?(follower, user)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
end
end
@ -241,6 +261,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
follower = User.get_cached_by_id(follower.id)
refute User.following?(follower, user)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
end
end
@ -261,17 +286,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
}&tags[]=foo&tags[]=bar"
)
%{conn: conn, user1: user1, user2: user2, user3: user3}
%{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
end
test "it appends specified tags to users with specified nicknames", %{
conn: conn,
admin: admin,
user1: user1,
user2: user2
} do
assert json_response(conn, :no_content)
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
log_entry = Repo.one(ModerationLog)
users =
[user1.nickname, user2.nickname]
|> Enum.map(&"@#{&1}")
|> Enum.join(", ")
tags = ["foo", "bar"] |> Enum.join(", ")
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} added tags: #{tags} to users: #{users}"
end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
@ -297,17 +335,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
}&tags[]=x&tags[]=z"
)
%{conn: conn, user1: user1, user2: user2, user3: user3}
%{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
end
test "it removes specified tags from users with specified nicknames", %{
conn: conn,
admin: admin,
user1: user1,
user2: user2
} do
assert json_response(conn, :no_content)
assert User.get_cached_by_id(user1.id).tags == []
assert User.get_cached_by_id(user2.id).tags == ["y"]
log_entry = Repo.one(ModerationLog)
users =
[user1.nickname, user2.nickname]
|> Enum.map(&"@#{&1}")
|> Enum.join(", ")
tags = ["x", "z"] |> Enum.join(", ")
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} removed tags: #{tags} from users: #{users}"
end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
@ -345,6 +396,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, 200) == %{
"is_admin" => true
}
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} made @#{user.nickname} admin"
end
test "/:right DELETE, can remove from a permission group" do
@ -360,6 +416,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, 200) == %{
"is_admin" => false
}
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} revoked admin role from @#{user.nickname}"
end
end
@ -372,10 +433,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|> assign(:user, admin)
|> put_req_header("accept", "application/json")
%{conn: conn}
%{conn: conn, admin: admin}
end
test "deactivates the user", %{conn: conn} do
test "deactivates the user", %{conn: conn, admin: admin} do
user = insert(:user)
conn =
@ -385,9 +446,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
user = User.get_cached_by_id(user.id)
assert user.info.deactivated == true
assert json_response(conn, :no_content)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated user @#{user.nickname}"
end
test "activates the user", %{conn: conn} do
test "activates the user", %{conn: conn, admin: admin} do
user = insert(:user, info: %{deactivated: true})
conn =
@ -397,6 +463,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
user = User.get_cached_by_id(user.id)
assert user.info.deactivated == false
assert json_response(conn, :no_content)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} activated user @#{user.nickname}"
end
test "returns 403 when requested by a non-admin", %{conn: conn} do
@ -987,6 +1058,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
"display_name" => HTML.strip_tags(user.name || user.nickname)
}
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated user @#{user.nickname}"
end
describe "GET /api/pleroma/admin/users/invite_token" do
@ -1172,25 +1248,35 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"status_ids" => [activity.id]
})
%{conn: assign(conn, :user, admin), id: report_id}
%{conn: assign(conn, :user, admin), id: report_id, admin: admin}
end
test "mark report as resolved", %{conn: conn, id: id} do
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"})
|> json_response(:ok)
assert response["state"] == "resolved"
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated report ##{id} with 'resolved' state"
end
test "closes report", %{conn: conn, id: id} do
test "closes report", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"})
|> json_response(:ok)
assert response["state"] == "closed"
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated report ##{id} with 'closed' state"
end
test "returns 400 when state is unknown", %{conn: conn, id: id} do
@ -1321,14 +1407,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
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)}
%{conn: assign(conn, :user, admin), admin: admin}
end
test "returns created dm", %{conn: conn} do
test "returns created dm", %{conn: conn, admin: admin} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
@ -1351,6 +1438,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert reporter.nickname in recipients
assert response["content"] == "I will check it out"
assert response["visibility"] == "direct"
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} responded with 'I will check it out' to report ##{
response["id"]
}"
end
test "returns 400 when status is missing", %{conn: conn} do
@ -1374,10 +1468,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
admin = insert(:user, info: %{is_admin: true})
activity = insert(:note_activity)
%{conn: assign(conn, :user, admin), id: activity.id}
%{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
end
test "toggle sensitive flag", %{conn: conn, id: id} do
test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
@ -1385,6 +1479,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert response["sensitive"]
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
@ -1393,7 +1492,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
refute response["sensitive"]
end
test "change visibility flag", %{conn: conn, id: id} do
test "change visibility flag", %{conn: conn, id: id, admin: admin} do
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
@ -1401,6 +1500,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert response["visibility"] == "public"
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
response =
conn
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
@ -1430,15 +1534,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
admin = insert(:user, info: %{is_admin: true})
activity = insert(:note_activity)
%{conn: assign(conn, :user, admin), id: activity.id}
%{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
end
test "deletes status", %{conn: conn, id: id} do
test "deletes status", %{conn: conn, id: id, admin: admin} do
conn
|> delete("/api/pleroma/admin/statuses/#{id}")
|> json_response(:ok)
refute Activity.get_by_id(id)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deleted status ##{id}"
end
test "returns error when status is not exist", %{conn: conn} do
@ -2139,6 +2248,108 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, 200) |> length() == 5
end
end
describe "GET /api/pleroma/admin/moderation_log" do
setup %{conn: conn} do
admin = insert(:user, info: %{is_admin: true})
%{conn: assign(conn, :user, admin), admin: admin}
end
test "returns the log", %{conn: conn, admin: admin} do
Repo.insert(%ModerationLog{
data: %{
actor: %{
"id" => admin.id,
"nickname" => admin.nickname,
"type" => "user"
},
action: "relay_follow",
target: "https://example.org/relay"
},
inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
})
Repo.insert(%ModerationLog{
data: %{
actor: %{
"id" => admin.id,
"nickname" => admin.nickname,
"type" => "user"
},
action: "relay_unfollow",
target: "https://example.org/relay"
},
inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
})
conn = get(conn, "/api/pleroma/admin/moderation_log")
response = json_response(conn, 200)
[first_entry, second_entry] = response
assert response |> length() == 2
assert first_entry["data"]["action"] == "relay_unfollow"
assert first_entry["message"] ==
"@#{admin.nickname} unfollowed relay: https://example.org/relay"
assert second_entry["data"]["action"] == "relay_follow"
assert second_entry["message"] ==
"@#{admin.nickname} followed relay: https://example.org/relay"
end
test "returns the log with pagination", %{conn: conn, admin: admin} do
Repo.insert(%ModerationLog{
data: %{
actor: %{
"id" => admin.id,
"nickname" => admin.nickname,
"type" => "user"
},
action: "relay_follow",
target: "https://example.org/relay"
},
inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
})
Repo.insert(%ModerationLog{
data: %{
actor: %{
"id" => admin.id,
"nickname" => admin.nickname,
"type" => "user"
},
action: "relay_unfollow",
target: "https://example.org/relay"
},
inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
})
conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
response1 = json_response(conn1, 200)
[first_entry] = response1
assert response1 |> length() == 1
assert first_entry["data"]["action"] == "relay_unfollow"
assert first_entry["message"] ==
"@#{admin.nickname} unfollowed relay: https://example.org/relay"
conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
response2 = json_response(conn2, 200)
[second_entry] = response2
assert response2 |> length() == 1
assert second_entry["data"]["action"] == "relay_follow"
assert second_entry["message"] ==
"@#{admin.nickname} followed relay: https://example.org/relay"
end
end
end
# Needed for testing