forked from YokaiRick/akkoma
Merge branch 'feature/moderation-log' into 'develop'
Log admin/moderator actions See merge request pleroma/pleroma!1582
This commit is contained in:
commit
897bd7a15e
11 changed files with 1187 additions and 41 deletions
|
@ -94,6 +94,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Relays: Added a task to list relay subscriptions.
|
- Relays: Added a task to list relay subscriptions.
|
||||||
- Mix Tasks: `mix pleroma.database fix_likes_collections`
|
- Mix Tasks: `mix pleroma.database fix_likes_collections`
|
||||||
- Federation: Remove `likes` from objects.
|
- Federation: Remove `likes` from objects.
|
||||||
|
- Admin API: Added moderation log
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
||||||
|
|
|
@ -694,3 +694,27 @@ Compile time settings (need instance reboot):
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/moderation_log`
|
||||||
|
### Get moderation log
|
||||||
|
- Method `GET`
|
||||||
|
- Params:
|
||||||
|
- *optional* `page`: **integer** page number
|
||||||
|
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"actor": {
|
||||||
|
"id": 1,
|
||||||
|
"nickname": "lain"
|
||||||
|
},
|
||||||
|
"action": "relay_follow"
|
||||||
|
},
|
||||||
|
"time": 1502812026, // timestamp
|
||||||
|
"message": "[2017-08-15 15:47:06] @nick0 followed relay: https://example.org/relay" // log message
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
433
lib/pleroma/moderation_log.ex
Normal file
433
lib/pleroma/moderation_log.ex
Normal file
|
@ -0,0 +1,433 @@
|
||||||
|
defmodule Pleroma.ModerationLog do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
schema "moderation_log" do
|
||||||
|
field(:data, :map)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_all(page, page_size) do
|
||||||
|
from(q in __MODULE__,
|
||||||
|
order_by: [desc: q.inserted_at],
|
||||||
|
limit: ^page_size,
|
||||||
|
offset: ^((page - 1) * page_size)
|
||||||
|
)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
subject: %User{} = subject,
|
||||||
|
action: action,
|
||||||
|
permission: permission
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
subject: user_to_map(subject),
|
||||||
|
action: action,
|
||||||
|
permission: permission
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
action: "report_update",
|
||||||
|
subject: %Activity{data: %{"type" => "Flag"}} = subject
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: "report_update",
|
||||||
|
subject: report_to_map(subject)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
action: "report_response",
|
||||||
|
subject: %Activity{} = subject,
|
||||||
|
text: text
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: "report_response",
|
||||||
|
subject: report_to_map(subject),
|
||||||
|
text: text
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
action: "status_update",
|
||||||
|
subject: %Activity{} = subject,
|
||||||
|
sensitive: sensitive,
|
||||||
|
visibility: visibility
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: "status_update",
|
||||||
|
subject: status_to_map(subject),
|
||||||
|
sensitive: sensitive,
|
||||||
|
visibility: visibility
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
action: "status_delete",
|
||||||
|
subject_id: subject_id
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: "status_delete",
|
||||||
|
subject_id: subject_id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec insert_log(%{actor: User, subject: User, action: String.t()}) ::
|
||||||
|
{:ok, ModerationLog} | {:error, any}
|
||||||
|
def insert_log(%{actor: %User{} = actor, subject: subject, action: action}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: action,
|
||||||
|
subject: user_to_map(subject)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec insert_log(%{actor: User, subjects: [User], action: String.t()}) ::
|
||||||
|
{:ok, ModerationLog} | {:error, any}
|
||||||
|
def insert_log(%{actor: %User{} = actor, subjects: subjects, action: action}) do
|
||||||
|
subjects = Enum.map(subjects, &user_to_map/1)
|
||||||
|
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: action,
|
||||||
|
subjects: subjects
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
followed: %User{} = followed,
|
||||||
|
follower: %User{} = follower,
|
||||||
|
action: "follow"
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: "follow",
|
||||||
|
followed: user_to_map(followed),
|
||||||
|
follower: user_to_map(follower)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
followed: %User{} = followed,
|
||||||
|
follower: %User{} = follower,
|
||||||
|
action: "unfollow"
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: "unfollow",
|
||||||
|
followed: user_to_map(followed),
|
||||||
|
follower: user_to_map(follower)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
nicknames: nicknames,
|
||||||
|
tags: tags,
|
||||||
|
action: action
|
||||||
|
}) do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
nicknames: nicknames,
|
||||||
|
tags: tags,
|
||||||
|
action: action
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_log(%{
|
||||||
|
actor: %User{} = actor,
|
||||||
|
action: action,
|
||||||
|
target: target
|
||||||
|
})
|
||||||
|
when action in ["relay_follow", "relay_unfollow"] do
|
||||||
|
Repo.insert(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
actor: user_to_map(actor),
|
||||||
|
action: action,
|
||||||
|
target: target
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_to_map(%User{} = user) do
|
||||||
|
user
|
||||||
|
|> Map.from_struct()
|
||||||
|
|> Map.take([:id, :nickname])
|
||||||
|
|> Map.put(:type, "user")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report_to_map(%Activity{} = report) do
|
||||||
|
%{
|
||||||
|
type: "report",
|
||||||
|
id: report.id,
|
||||||
|
state: report.data["state"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_to_map(%Activity{} = status) do
|
||||||
|
%{
|
||||||
|
type: "status",
|
||||||
|
id: status.id
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => action,
|
||||||
|
"followed" => %{"nickname" => followed_nickname},
|
||||||
|
"follower" => %{"nickname" => follower_nickname}
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} made @#{follower_nickname} #{action} @#{followed_nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "delete",
|
||||||
|
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} deleted user @#{subject_nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "create",
|
||||||
|
"subjects" => subjects
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
nicknames =
|
||||||
|
subjects
|
||||||
|
|> Enum.map(&"@#{&1["nickname"]}")
|
||||||
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
"@#{actor_nickname} created users: #{nicknames}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "activate",
|
||||||
|
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} activated user @#{subject_nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "deactivate",
|
||||||
|
"subject" => %{"nickname" => subject_nickname, "type" => "user"}
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} deactivated user @#{subject_nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"nicknames" => nicknames,
|
||||||
|
"tags" => tags,
|
||||||
|
"action" => "tag"
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
nicknames_string =
|
||||||
|
nicknames
|
||||||
|
|> Enum.map(&"@#{&1}")
|
||||||
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
tags_string = tags |> Enum.join(", ")
|
||||||
|
|
||||||
|
"@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_string}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"nicknames" => nicknames,
|
||||||
|
"tags" => tags,
|
||||||
|
"action" => "untag"
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
nicknames_string =
|
||||||
|
nicknames
|
||||||
|
|> Enum.map(&"@#{&1}")
|
||||||
|
|> Enum.join(", ")
|
||||||
|
|
||||||
|
tags_string = tags |> Enum.join(", ")
|
||||||
|
|
||||||
|
"@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_string}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "grant",
|
||||||
|
"subject" => %{"nickname" => subject_nickname},
|
||||||
|
"permission" => permission
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} made @#{subject_nickname} #{permission}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "revoke",
|
||||||
|
"subject" => %{"nickname" => subject_nickname},
|
||||||
|
"permission" => permission
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} revoked #{permission} role from @#{subject_nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "relay_follow",
|
||||||
|
"target" => target
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} followed relay: #{target}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "relay_unfollow",
|
||||||
|
"target" => target
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} unfollowed relay: #{target}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "report_update",
|
||||||
|
"subject" => %{"id" => subject_id, "state" => state, "type" => "report"}
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} updated report ##{subject_id} with '#{state}' state"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "report_response",
|
||||||
|
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||||
|
"text" => text
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} responded with '#{text}' to report ##{subject_id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "status_update",
|
||||||
|
"subject" => %{"id" => subject_id, "type" => "status"},
|
||||||
|
"sensitive" => nil,
|
||||||
|
"visibility" => visibility
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} updated status ##{subject_id}, set visibility: '#{visibility}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "status_update",
|
||||||
|
"subject" => %{"id" => subject_id, "type" => "status"},
|
||||||
|
"sensitive" => sensitive,
|
||||||
|
"visibility" => nil
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "status_update",
|
||||||
|
"subject" => %{"id" => subject_id, "type" => "status"},
|
||||||
|
"sensitive" => sensitive,
|
||||||
|
"visibility" => visibility
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{
|
||||||
|
visibility
|
||||||
|
}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "status_delete",
|
||||||
|
"subject_id" => subject_id
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} deleted status ##{subject_id}"
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -12,6 +13,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.Config
|
alias Pleroma.Web.AdminAPI.Config
|
||||||
alias Pleroma.Web.AdminAPI.ConfigView
|
alias Pleroma.Web.AdminAPI.ConfigView
|
||||||
|
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||||
alias Pleroma.Web.AdminAPI.ReportView
|
alias Pleroma.Web.AdminAPI.ReportView
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
@ -25,35 +27,61 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def user_delete(conn, %{"nickname" => nickname}) do
|
def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
User.get_cached_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|> User.delete()
|
User.delete(user)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: user,
|
||||||
|
action: "delete"
|
||||||
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(nickname)
|
|> json(nickname)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"follower" => follower_nick,
|
||||||
|
"followed" => followed_nick
|
||||||
|
}) do
|
||||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||||
User.follow(follower, followed)
|
User.follow(follower, followed)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
followed: followed,
|
||||||
|
follower: follower,
|
||||||
|
action: "follow"
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json("ok")
|
|> json("ok")
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
|
def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"follower" => follower_nick,
|
||||||
|
"followed" => followed_nick
|
||||||
|
}) do
|
||||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||||
User.unfollow(follower, followed)
|
User.unfollow(follower, followed)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
followed: followed,
|
||||||
|
follower: follower,
|
||||||
|
action: "unfollow"
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json("ok")
|
|> json("ok")
|
||||||
end
|
end
|
||||||
|
|
||||||
def users_create(conn, %{"users" => users}) do
|
def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||||
changesets =
|
changesets =
|
||||||
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
||||||
user_data = %{
|
user_data = %{
|
||||||
|
@ -78,10 +106,17 @@ def users_create(conn, %{"users" => users}) do
|
||||||
|> Map.values()
|
|> Map.values()
|
||||||
|> Enum.map(fn user ->
|
|> Enum.map(fn user ->
|
||||||
{:ok, user} = User.post_register_action(user)
|
{:ok, user} = User.post_register_action(user)
|
||||||
|
|
||||||
user
|
user
|
||||||
end)
|
end)
|
||||||
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subjects: Map.values(users),
|
||||||
|
action: "create"
|
||||||
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(res)
|
|> json(res)
|
||||||
|
|
||||||
|
@ -129,23 +164,47 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_toggle_activation(conn, %{"nickname" => nickname}) do
|
def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
user = User.get_cached_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
|
{:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
|
||||||
|
|
||||||
|
action = if user.info.deactivated, do: "activate", else: "deactivate"
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: user,
|
||||||
|
action: action
|
||||||
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> json(AccountView.render("show.json", %{user: updated_user}))
|
|> json(AccountView.render("show.json", %{user: updated_user}))
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||||
with {:ok, _} <- User.tag(nicknames, tags),
|
with {:ok, _} <- User.tag(nicknames, tags) do
|
||||||
do: json_response(conn, :no_content, "")
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
nicknames: nicknames,
|
||||||
|
tags: tags,
|
||||||
|
action: "tag"
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||||
with {:ok, _} <- User.untag(nicknames, tags),
|
with {:ok, _} <- User.untag(nicknames, tags) do
|
||||||
do: json_response(conn, :no_content, "")
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
nicknames: nicknames,
|
||||||
|
tags: tags,
|
||||||
|
action: "untag"
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_users(conn, params) do
|
def list_users(conn, params) do
|
||||||
|
@ -186,7 +245,10 @@ defp maybe_parse_filters(filters) do
|
||||||
|> Enum.into(%{}, &{&1, true})
|
|> Enum.into(%{}, &{&1, true})
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
|
def right_add(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"permission_group" => permission_group,
|
||||||
|
"nickname" => nickname
|
||||||
|
})
|
||||||
when permission_group in ["moderator", "admin"] do
|
when permission_group in ["moderator", "admin"] do
|
||||||
user = User.get_cached_by_nickname(nickname)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
|
@ -201,6 +263,13 @@ def right_add(conn, %{"permission_group" => permission_group, "nickname" => nick
|
||||||
|> Ecto.Changeset.change()
|
|> Ecto.Changeset.change()
|
||||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
|> Ecto.Changeset.put_embed(:info, info_cng)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "grant",
|
||||||
|
actor: admin,
|
||||||
|
subject: user,
|
||||||
|
permission: permission_group
|
||||||
|
})
|
||||||
|
|
||||||
{:ok, _user} = User.update_and_set_cache(cng)
|
{:ok, _user} = User.update_and_set_cache(cng)
|
||||||
|
|
||||||
json(conn, info)
|
json(conn, info)
|
||||||
|
@ -221,7 +290,7 @@ def right_get(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_delete(
|
def right_delete(
|
||||||
%{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
|
%{assigns: %{user: %User{:nickname => admin_nickname} = admin}} = conn,
|
||||||
%{
|
%{
|
||||||
"permission_group" => permission_group,
|
"permission_group" => permission_group,
|
||||||
"nickname" => nickname
|
"nickname" => nickname
|
||||||
|
@ -245,6 +314,13 @@ def right_delete(
|
||||||
|
|
||||||
{:ok, _user} = User.update_and_set_cache(cng)
|
{:ok, _user} = User.update_and_set_cache(cng)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "revoke",
|
||||||
|
actor: admin,
|
||||||
|
subject: user,
|
||||||
|
permission: permission_group
|
||||||
|
})
|
||||||
|
|
||||||
json(conn, info)
|
json(conn, info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -253,15 +329,33 @@ def right_delete(conn, _) do
|
||||||
render_error(conn, :not_found, "No such permission_group")
|
render_error(conn, :not_found, "No such permission_group")
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
|
def set_activation_status(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"nickname" => nickname,
|
||||||
|
"status" => status
|
||||||
|
}) do
|
||||||
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
|
with {:ok, status} <- Ecto.Type.cast(:boolean, status),
|
||||||
%User{} = user <- User.get_cached_by_nickname(nickname),
|
%User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, _} <- User.deactivate(user, !status),
|
{:ok, _} <- User.deactivate(user, !status) do
|
||||||
do: json_response(conn, :no_content, "")
|
action = if(user.info.deactivated, do: "activate", else: "deactivate")
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: user,
|
||||||
|
action: action
|
||||||
|
})
|
||||||
|
|
||||||
|
json_response(conn, :no_content, "")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def relay_follow(conn, %{"relay_url" => target}) do
|
def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
||||||
with {:ok, _message} <- Relay.follow(target) do
|
with {:ok, _message} <- Relay.follow(target) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "relay_follow",
|
||||||
|
actor: admin,
|
||||||
|
target: target
|
||||||
|
})
|
||||||
|
|
||||||
json(conn, target)
|
json(conn, target)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -271,8 +365,14 @@ def relay_follow(conn, %{"relay_url" => target}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def relay_unfollow(conn, %{"relay_url" => target}) do
|
def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
|
||||||
with {:ok, _message} <- Relay.unfollow(target) do
|
with {:ok, _message} <- Relay.unfollow(target) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "relay_unfollow",
|
||||||
|
actor: admin,
|
||||||
|
target: target
|
||||||
|
})
|
||||||
|
|
||||||
json(conn, target)
|
json(conn, target)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -363,8 +463,14 @@ def report_show(conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_update_state(conn, %{"id" => id, "state" => state}) do
|
def report_update_state(%{assigns: %{user: admin}} = conn, %{"id" => id, "state" => state}) do
|
||||||
with {:ok, report} <- CommonAPI.update_report_state(id, state) do
|
with {:ok, report} <- CommonAPI.update_report_state(id, state) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: report
|
||||||
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(ReportView)
|
|> put_view(ReportView)
|
||||||
|> render("show.json", %{report: report})
|
|> render("show.json", %{report: report})
|
||||||
|
@ -381,6 +487,13 @@ def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, params)
|
{:ok, activity} = CommonAPI.post(user, params)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "report_response",
|
||||||
|
actor: user,
|
||||||
|
subject: activity,
|
||||||
|
text: params["status"]
|
||||||
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("status.json", %{activity: activity})
|
|> render("status.json", %{activity: activity})
|
||||||
|
@ -393,8 +506,18 @@ def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_update(conn, %{"id" => id} = params) do
|
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
||||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||||
|
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "status_update",
|
||||||
|
actor: admin,
|
||||||
|
subject: activity,
|
||||||
|
sensitive: sensitive,
|
||||||
|
visibility: params["visibility"]
|
||||||
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> render("status.json", %{activity: activity})
|
|> render("status.json", %{activity: activity})
|
||||||
|
@ -403,10 +526,26 @@ def status_update(conn, %{"id" => id} = params) do
|
||||||
|
|
||||||
def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
action: "status_delete",
|
||||||
|
actor: user,
|
||||||
|
subject_id: id
|
||||||
|
})
|
||||||
|
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list_log(conn, params) do
|
||||||
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
log = ModerationLog.get_all(page, page_size)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(ModerationLogView)
|
||||||
|
|> render("index.json", %{log: log})
|
||||||
|
end
|
||||||
|
|
||||||
def migrate_to_db(conn, _params) do
|
def migrate_to_db(conn, _params) do
|
||||||
Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
|
Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
|
|
26
lib/pleroma/web/admin_api/views/moderation_log_view.ex
Normal file
26
lib/pleroma/web/admin_api/views/moderation_log_view.ex
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# 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.ModerationLogView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
|
||||||
|
def render("index.json", %{log: log}) do
|
||||||
|
render_many(log, __MODULE__, "show.json", as: :log_entry)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{log_entry: log_entry}) do
|
||||||
|
time =
|
||||||
|
log_entry.inserted_at
|
||||||
|
|> DateTime.from_naive!("Etc/UTC")
|
||||||
|
|> DateTime.to_unix()
|
||||||
|
|
||||||
|
%{
|
||||||
|
data: log_entry.data,
|
||||||
|
time: time,
|
||||||
|
message: ModerationLog.get_log_entry_message(log_entry)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -93,8 +93,7 @@ def attachments_from_ids_descs(ids, descs_str) do
|
||||||
Activity.t() | nil,
|
Activity.t() | nil,
|
||||||
String.t(),
|
String.t(),
|
||||||
Participation.t() | nil
|
Participation.t() | nil
|
||||||
) ::
|
) :: {list(String.t()), list(String.t())}
|
||||||
{list(String.t()), list(String.t())}
|
|
||||||
|
|
||||||
def get_to_and_cc(_, _, _, _, %Participation{} = participation) do
|
def get_to_and_cc(_, _, _, _, %Participation{} = participation) do
|
||||||
participation = Repo.preload(participation, :recipients)
|
participation = Repo.preload(participation, :recipients)
|
||||||
|
|
|
@ -37,8 +37,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
|
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
|
||||||
with {_, %User{} = user} <-
|
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
|
||||||
{:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
|
|
||||||
RedirectController.redirector_with_meta(conn, %{user: user})
|
RedirectController.redirector_with_meta(conn, %{user: user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -198,6 +198,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/config", AdminAPIController, :config_update)
|
post("/config", AdminAPIController, :config_update)
|
||||||
get("/config/migrate_to_db", AdminAPIController, :migrate_to_db)
|
get("/config/migrate_to_db", AdminAPIController, :migrate_to_db)
|
||||||
get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
|
get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
|
||||||
|
|
||||||
|
get("/moderation_log", AdminAPIController, :list_log)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.TwitterAPI do
|
scope "/", Pleroma.Web.TwitterAPI do
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateModerationLog do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:moderation_log) do
|
||||||
|
add(:data, :map)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
301
test/moderation_log_test.exs
Normal file
301
test/moderation_log_test.exs
Normal 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
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
@ -24,6 +26,14 @@ test "Delete" do
|
||||||
|> put_req_header("accept", "application/json")
|
|> put_req_header("accept", "application/json")
|
||||||
|> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
|
|> 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
|
assert json_response(conn, 200) == user.nickname
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,6 +61,11 @@ test "Create" do
|
||||||
|
|
||||||
response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
|
response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
|
||||||
assert response == ["success", "success"]
|
assert response == ["success", "success"]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} created users: @lain2, @lain"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Cannot create user with exisiting email" do
|
test "Cannot create user with exisiting email" do
|
||||||
|
@ -218,6 +233,11 @@ test "allows to force-follow another user" do
|
||||||
follower = User.get_cached_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
assert User.following?(follower, user)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -241,6 +261,11 @@ test "allows to force-unfollow another user" do
|
||||||
follower = User.get_cached_by_id(follower.id)
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
refute User.following?(follower, user)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -261,17 +286,30 @@ test "allows to force-unfollow another user" do
|
||||||
}&tags[]=foo&tags[]=bar"
|
}&tags[]=foo&tags[]=bar"
|
||||||
)
|
)
|
||||||
|
|
||||||
%{conn: conn, user1: user1, user2: user2, user3: user3}
|
%{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it appends specified tags to users with specified nicknames", %{
|
test "it appends specified tags to users with specified nicknames", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
|
admin: admin,
|
||||||
user1: user1,
|
user1: user1,
|
||||||
user2: user2
|
user2: user2
|
||||||
} do
|
} do
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
|
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
|
||||||
assert User.get_cached_by_id(user2.id).tags == ["y", "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
|
end
|
||||||
|
|
||||||
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
||||||
|
@ -297,17 +335,30 @@ test "it does not modify tags of not specified users", %{conn: conn, user3: user
|
||||||
}&tags[]=x&tags[]=z"
|
}&tags[]=x&tags[]=z"
|
||||||
)
|
)
|
||||||
|
|
||||||
%{conn: conn, user1: user1, user2: user2, user3: user3}
|
%{conn: conn, admin: admin, user1: user1, user2: user2, user3: user3}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it removes specified tags from users with specified nicknames", %{
|
test "it removes specified tags from users with specified nicknames", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
|
admin: admin,
|
||||||
user1: user1,
|
user1: user1,
|
||||||
user2: user2
|
user2: user2
|
||||||
} do
|
} do
|
||||||
assert json_response(conn, :no_content)
|
assert json_response(conn, :no_content)
|
||||||
assert User.get_cached_by_id(user1.id).tags == []
|
assert User.get_cached_by_id(user1.id).tags == []
|
||||||
assert User.get_cached_by_id(user2.id).tags == ["y"]
|
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
|
end
|
||||||
|
|
||||||
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
|
||||||
|
@ -345,6 +396,11 @@ test "/:right POST, can add to a permission group" do
|
||||||
assert json_response(conn, 200) == %{
|
assert json_response(conn, 200) == %{
|
||||||
"is_admin" => true
|
"is_admin" => true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} made @#{user.nickname} admin"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "/:right DELETE, can remove from a permission group" do
|
test "/:right DELETE, can remove from a permission group" do
|
||||||
|
@ -360,6 +416,11 @@ test "/:right DELETE, can remove from a permission group" do
|
||||||
assert json_response(conn, 200) == %{
|
assert json_response(conn, 200) == %{
|
||||||
"is_admin" => false
|
"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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -372,10 +433,10 @@ test "/:right DELETE, can remove from a permission group" do
|
||||||
|> assign(:user, admin)
|
|> assign(:user, admin)
|
||||||
|> put_req_header("accept", "application/json")
|
|> put_req_header("accept", "application/json")
|
||||||
|
|
||||||
%{conn: conn}
|
%{conn: conn, admin: admin}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deactivates the user", %{conn: conn} do
|
test "deactivates the user", %{conn: conn, admin: admin} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
|
@ -385,9 +446,14 @@ test "deactivates the user", %{conn: conn} do
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.deactivated == true
|
assert user.info.deactivated == true
|
||||||
assert json_response(conn, :no_content)
|
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
|
end
|
||||||
|
|
||||||
test "activates the user", %{conn: conn} do
|
test "activates the user", %{conn: conn, admin: admin} do
|
||||||
user = insert(:user, info: %{deactivated: true})
|
user = insert(:user, info: %{deactivated: true})
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
|
@ -397,6 +463,11 @@ test "activates the user", %{conn: conn} do
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
assert user.info.deactivated == false
|
assert user.info.deactivated == false
|
||||||
assert json_response(conn, :no_content)
|
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
|
end
|
||||||
|
|
||||||
test "returns 403 when requested by a non-admin", %{conn: conn} do
|
test "returns 403 when requested by a non-admin", %{conn: conn} do
|
||||||
|
@ -987,6 +1058,11 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"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
|
end
|
||||||
|
|
||||||
describe "GET /api/pleroma/admin/users/invite_token" do
|
describe "GET /api/pleroma/admin/users/invite_token" do
|
||||||
|
@ -1172,25 +1248,35 @@ test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
"status_ids" => [activity.id]
|
"status_ids" => [activity.id]
|
||||||
})
|
})
|
||||||
|
|
||||||
%{conn: assign(conn, :user, admin), id: report_id}
|
%{conn: assign(conn, :user, admin), id: report_id, admin: admin}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "mark report as resolved", %{conn: conn, id: id} do
|
test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"})
|
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "resolved"})
|
||||||
|> json_response(:ok)
|
|> json_response(:ok)
|
||||||
|
|
||||||
assert response["state"] == "resolved"
|
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
|
end
|
||||||
|
|
||||||
test "closes report", %{conn: conn, id: id} do
|
test "closes report", %{conn: conn, id: id, admin: admin} do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"})
|
|> put("/api/pleroma/admin/reports/#{id}", %{"state" => "closed"})
|
||||||
|> json_response(:ok)
|
|> json_response(:ok)
|
||||||
|
|
||||||
assert response["state"] == "closed"
|
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
|
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
|
||||||
|
@ -1321,14 +1407,15 @@ test "returns 403 when requested by anonymous" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
describe "POST /api/pleroma/admin/reports/:id/respond" do
|
describe "POST /api/pleroma/admin/reports/:id/respond" do
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
%{conn: assign(conn, :user, admin)}
|
%{conn: assign(conn, :user, admin), admin: admin}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns created dm", %{conn: conn} do
|
test "returns created dm", %{conn: conn, admin: admin} do
|
||||||
[reporter, target_user] = insert_pair(:user)
|
[reporter, target_user] = insert_pair(:user)
|
||||||
activity = insert(:note_activity, user: target_user)
|
activity = insert(:note_activity, user: target_user)
|
||||||
|
|
||||||
|
@ -1351,6 +1438,13 @@ test "returns created dm", %{conn: conn} do
|
||||||
assert reporter.nickname in recipients
|
assert reporter.nickname in recipients
|
||||||
assert response["content"] == "I will check it out"
|
assert response["content"] == "I will check it out"
|
||||||
assert response["visibility"] == "direct"
|
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
|
end
|
||||||
|
|
||||||
test "returns 400 when status is missing", %{conn: conn} do
|
test "returns 400 when status is missing", %{conn: conn} do
|
||||||
|
@ -1374,10 +1468,10 @@ test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
%{conn: assign(conn, :user, admin), id: activity.id}
|
%{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "toggle sensitive flag", %{conn: conn, id: id} do
|
test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
|
||||||
|
@ -1385,6 +1479,11 @@ test "toggle sensitive flag", %{conn: conn, id: id} do
|
||||||
|
|
||||||
assert response["sensitive"]
|
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 =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|
|> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
|
||||||
|
@ -1393,7 +1492,7 @@ test "toggle sensitive flag", %{conn: conn, id: id} do
|
||||||
refute response["sensitive"]
|
refute response["sensitive"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "change visibility flag", %{conn: conn, id: id} do
|
test "change visibility flag", %{conn: conn, id: id, admin: admin} do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
|
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
|
||||||
|
@ -1401,6 +1500,11 @@ test "change visibility flag", %{conn: conn, id: id} do
|
||||||
|
|
||||||
assert response["visibility"] == "public"
|
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 =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
|
|> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
|
||||||
|
@ -1430,15 +1534,20 @@ test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
|
||||||
admin = insert(:user, info: %{is_admin: true})
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
%{conn: assign(conn, :user, admin), id: activity.id}
|
%{conn: assign(conn, :user, admin), id: activity.id, admin: admin}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deletes status", %{conn: conn, id: id} do
|
test "deletes status", %{conn: conn, id: id, admin: admin} do
|
||||||
conn
|
conn
|
||||||
|> delete("/api/pleroma/admin/statuses/#{id}")
|
|> delete("/api/pleroma/admin/statuses/#{id}")
|
||||||
|> json_response(:ok)
|
|> json_response(:ok)
|
||||||
|
|
||||||
refute Activity.get_by_id(id)
|
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
|
end
|
||||||
|
|
||||||
test "returns error when status is not exist", %{conn: conn} do
|
test "returns error when status is not exist", %{conn: conn} do
|
||||||
|
@ -2139,6 +2248,108 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do
|
||||||
assert json_response(conn, 200) |> length() == 5
|
assert json_response(conn, 200) |> length() == 5
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
# Needed for testing
|
# Needed for testing
|
||||||
|
|
Loading…
Reference in a new issue