Merge branch 'develop' into fix/disable-rate-limiter-for-socket-localhost
This commit is contained in:
commit
45180d4c60
132 changed files with 598 additions and 1680 deletions
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
|
||||
|
|
@ -48,6 +49,8 @@ defmodule Pleroma.Activity do
|
|||
has_one(:user_actor, User, on_delete: :nothing, foreign_key: :id)
|
||||
# This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark
|
||||
has_one(:bookmark, Bookmark)
|
||||
# This is a fake relation, do not use outside of with_preloaded_report_notes
|
||||
has_many(:report_notes, ReportNote)
|
||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||
|
||||
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
||||
|
|
@ -114,6 +117,16 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def with_preloaded_bookmark(query, _), do: query
|
||||
|
||||
def with_preloaded_report_notes(query) do
|
||||
from([a] in query,
|
||||
left_join: r in ReportNote,
|
||||
on: a.id == r.activity_id,
|
||||
preload: [report_notes: r]
|
||||
)
|
||||
end
|
||||
|
||||
def with_preloaded_report_notes(query, _), do: query
|
||||
|
||||
def with_set_thread_muted_field(query, %User{} = user) do
|
||||
from([a] in query,
|
||||
left_join: tm in ThreadMute,
|
||||
|
|
|
|||
35
lib/pleroma/captcha/native.ex
Normal file
35
lib/pleroma/captcha/native.ex
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Captcha.Native do
|
||||
import Pleroma.Web.Gettext
|
||||
alias Pleroma.Captcha.Service
|
||||
@behaviour Service
|
||||
|
||||
@impl Service
|
||||
def new do
|
||||
case Captcha.get() do
|
||||
{:timeout} ->
|
||||
%{error: dgettext("errors", "Captcha timeout")}
|
||||
|
||||
{:ok, answer_data, img_binary} ->
|
||||
%{
|
||||
type: :native,
|
||||
token: token(),
|
||||
url: "data:image/png;base64," <> Base.encode64(img_binary),
|
||||
answer_data: answer_data
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Service
|
||||
def validate(_token, captcha, captcha) when not is_nil(captcha), do: :ok
|
||||
def validate(_token, _captcha, _answer), do: {:error, dgettext("errors", "Invalid CAPTCHA")}
|
||||
|
||||
defp token do
|
||||
10
|
||||
|> :crypto.strong_rand_bytes()
|
||||
|> Base.url_encode64(padding: false)
|
||||
end
|
||||
end
|
||||
|
|
@ -128,17 +128,35 @@ defmodule Pleroma.ModerationLog do
|
|||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_response",
|
||||
action: "report_note",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_response",
|
||||
"action" => "report_note",
|
||||
"subject" => report_to_map(subject),
|
||||
"text" => text,
|
||||
"message" => ""
|
||||
"text" => text
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_note_delete",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_note_delete",
|
||||
"subject" => report_to_map(subject),
|
||||
"text" => text
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
|
|
@ -556,12 +574,24 @@ defmodule Pleroma.ModerationLog do
|
|||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_response",
|
||||
"action" => "report_note",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} responded with '#{text}' to report ##{subject_id}"
|
||||
"@#{actor_nickname} added note '#{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" => "report_note_delete",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
|
|
|
|||
|
|
@ -38,7 +38,10 @@ defmodule Pleroma.Pagination do
|
|||
end
|
||||
|
||||
def fetch_paginated(query, %{"total" => true} = params, :offset, table_binding) do
|
||||
total = Repo.aggregate(query, :count, :id)
|
||||
total =
|
||||
query
|
||||
|> Ecto.Query.exclude(:left_join)
|
||||
|> Repo.aggregate(:count, :id)
|
||||
|
||||
%{
|
||||
total: total,
|
||||
|
|
|
|||
48
lib/pleroma/report_note.ex
Normal file
48
lib/pleroma/report_note.ex
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ReportNote do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.User
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "report_notes" do
|
||||
field(:content, :string)
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@spec create(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t(), String.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Changeset.t()}
|
||||
def create(user_id, activity_id, content) do
|
||||
attrs = %{
|
||||
user_id: user_id,
|
||||
activity_id: activity_id,
|
||||
content: content
|
||||
}
|
||||
|
||||
%ReportNote{}
|
||||
|> cast(attrs, [:user_id, :activity_id, :content])
|
||||
|> validate_required([:user_id, :activity_id, :content])
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@spec destroy(FlakeId.Ecto.CompatType.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Changeset.t()}
|
||||
def destroy(id) do
|
||||
from(r in ReportNote, where: r.id == ^id)
|
||||
|> Repo.one()
|
||||
|> Repo.delete()
|
||||
end
|
||||
end
|
||||
|
|
@ -1068,6 +1068,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> Activity.with_preloaded_bookmark(opts["user"])
|
||||
end
|
||||
|
||||
defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
|
||||
query
|
||||
|> Activity.with_preloaded_report_notes()
|
||||
end
|
||||
|
||||
defp maybe_preload_report_notes(query, _), do: query
|
||||
|
||||
defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
|
||||
|
||||
defp maybe_set_thread_muted_field(query, opts) do
|
||||
|
|
@ -1121,6 +1128,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Activity
|
||||
|> maybe_preload_objects(opts)
|
||||
|> maybe_preload_bookmarks(opts)
|
||||
|> maybe_preload_report_notes(opts)
|
||||
|> maybe_set_thread_muted_field(opts)
|
||||
|> maybe_order(opts)
|
||||
|> restrict_recipients(recipients, opts["user"])
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
alias Pleroma.HTTP
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
|
@ -188,31 +189,35 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|
||||
recipients = recipients(actor, activity)
|
||||
|
||||
recipients
|
||||
|> Enum.filter(&User.ap_enabled?/1)
|
||||
|> Enum.map(fn %{source_data: data} -> data["inbox"] end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|> Enum.each(fn {inbox, unreachable_since} ->
|
||||
%User{ap_id: ap_id} =
|
||||
Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
|
||||
inboxes =
|
||||
recipients
|
||||
|> Enum.filter(&User.ap_enabled?/1)
|
||||
|> Enum.map(fn %{source_data: data} -> data["inbox"] end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|
||||
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
||||
# instance would only accept a first message for the first recipient and ignore the rest.
|
||||
cc = get_cc_ap_ids(ap_id, recipients)
|
||||
Repo.checkout(fn ->
|
||||
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
||||
%User{ap_id: ap_id} =
|
||||
Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end)
|
||||
|
||||
json =
|
||||
data
|
||||
|> Map.put("cc", cc)
|
||||
|> Jason.encode!()
|
||||
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
||||
# instance would only accept a first message for the first recipient and ignore the rest.
|
||||
cc = get_cc_ap_ids(ap_id, recipients)
|
||||
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor_id: actor.id,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
})
|
||||
json =
|
||||
data
|
||||
|> Map.put("cc", cc)
|
||||
|> Jason.encode!()
|
||||
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor_id: actor.id,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -787,6 +787,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
params
|
||||
|> Map.put("type", "Flag")
|
||||
|> Map.put("skip_preload", true)
|
||||
|> Map.put("preload_report_notes", true)
|
||||
|> Map.put("total", true)
|
||||
|> Map.put("limit", page_size)
|
||||
|> Map.put("offset", (page - 1) * page_size)
|
||||
|
|
|
|||
|
|
@ -201,7 +201,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
%{
|
||||
"id" => "#{user.ap_id}/followers",
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => total,
|
||||
"first" =>
|
||||
if showing_items do
|
||||
collection(followers, "#{user.ap_id}/followers", 1, showing_items, total)
|
||||
|
|
@ -209,6 +208,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"#{user.ap_id}/followers?page=1"
|
||||
end
|
||||
}
|
||||
|> maybe_put_total_items(showing_count, total)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
|
@ -251,6 +251,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
defp maybe_put_total_items(map, false, _total), do: map
|
||||
|
||||
defp maybe_put_total_items(map, true, total) do
|
||||
Map.put(map, "totalItems", total)
|
||||
end
|
||||
|
||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||
offset = (page - 1) * 10
|
||||
items = Enum.slice(collection, offset, 10)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -240,7 +241,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
||||
|> render("index.json", %{activities: activities, as: :activity})
|
||||
end
|
||||
|
||||
|
|
@ -643,9 +644,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
def list_reports(conn, params) do
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
reports = Utils.get_reports(params, page, page_size)
|
||||
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("index.json", %{reports: Utils.get_reports(params, page, page_size)})
|
||||
|> render("index.json", %{reports: reports})
|
||||
end
|
||||
|
||||
def list_grouped_reports(conn, _params) do
|
||||
|
|
@ -689,32 +692,39 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
end
|
||||
end
|
||||
|
||||
def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
|
||||
with false <- is_nil(params["status"]),
|
||||
%Activity{} <- Activity.get_by_id(id) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("in_reply_to_status_id", id)
|
||||
|> Map.put("visibility", "direct")
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, params)
|
||||
|
||||
def report_notes_create(%{assigns: %{user: user}} = conn, %{
|
||||
"id" => report_id,
|
||||
"content" => content
|
||||
}) do
|
||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_response",
|
||||
action: "report_note",
|
||||
actor: user,
|
||||
subject: activity,
|
||||
text: params["status"]
|
||||
subject: Activity.get_by_id(report_id),
|
||||
text: content
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(StatusView)
|
||||
|> render("show.json", %{activity: activity})
|
||||
json_response(conn, :no_content, "")
|
||||
else
|
||||
true ->
|
||||
{:param_cast, nil}
|
||||
_ -> json_response(conn, :bad_request, "")
|
||||
end
|
||||
end
|
||||
|
||||
nil ->
|
||||
{:error, :not_found}
|
||||
def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
||||
"id" => note_id,
|
||||
"report_id" => report_id
|
||||
}) do
|
||||
with {:ok, note} <- ReportNote.destroy(note_id) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_note_delete",
|
||||
actor: user,
|
||||
subject: Activity.get_by_id(report_id),
|
||||
text: note.content
|
||||
})
|
||||
|
||||
json_response(conn, :no_content, "")
|
||||
else
|
||||
_ -> json_response(conn, :bad_request, "")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||
content: content,
|
||||
created_at: created_at,
|
||||
statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}),
|
||||
state: report.data["state"]
|
||||
state: report.data["state"],
|
||||
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -69,6 +70,28 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||
}
|
||||
end
|
||||
|
||||
def render("index_notes.json", %{notes: notes}) when is_list(notes) do
|
||||
Enum.map(notes, &render(__MODULE__, "show_note.json", &1))
|
||||
end
|
||||
|
||||
def render("index_notes.json", _), do: []
|
||||
|
||||
def render("show_note.json", %{
|
||||
id: id,
|
||||
content: content,
|
||||
user_id: user_id,
|
||||
inserted_at: inserted_at
|
||||
}) do
|
||||
user = User.get_by_id(user_id)
|
||||
|
||||
%{
|
||||
id: id,
|
||||
content: content,
|
||||
user: merge_account_views(user),
|
||||
created_at: Utils.to_masto_date(inserted_at)
|
||||
}
|
||||
end
|
||||
|
||||
defp merge_account_views(%User{} = user) do
|
||||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|
||||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
|
||||
|
|
|
|||
42
lib/pleroma/web/admin_api/views/status_view.ex
Normal file
42
lib/pleroma/web/admin_api/views/status_view.ex
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# 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.StatusView do
|
||||
use Pleroma.Web, :view
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.User
|
||||
|
||||
def render("index.json", opts) do
|
||||
render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
user = get_user(activity.data["actor"])
|
||||
|
||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json", opts)
|
||||
|> Map.merge(%{account: merge_account_views(user)})
|
||||
end
|
||||
|
||||
defp merge_account_views(%User{} = user) do
|
||||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|
||||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
|
||||
end
|
||||
|
||||
defp merge_account_views(_), do: %{}
|
||||
|
||||
defp get_user(ap_id) do
|
||||
cond do
|
||||
user = User.get_cached_by_ap_id(ap_id) ->
|
||||
user
|
||||
|
||||
user = User.get_by_guessed_nickname(ap_id) ->
|
||||
user
|
||||
|
||||
true ->
|
||||
User.error_user(ap_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -79,7 +79,9 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
|||
if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do
|
||||
{:ok, scopes}
|
||||
else
|
||||
{:error, :unsupported_scopes}
|
||||
# Gracefully dropping admin scopes from requested scopes if user isn't an admin (not raising)
|
||||
scopes = scopes -- OAuthScopesPlug.filter_descendants(scopes, ["admin"])
|
||||
validate(scopes, app_scopes, user)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -187,7 +187,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/grouped_reports", AdminAPIController, :list_grouped_reports)
|
||||
get("/reports/:id", AdminAPIController, :report_show)
|
||||
patch("/reports", AdminAPIController, :reports_update)
|
||||
post("/reports/:id/respond", AdminAPIController, :report_respond)
|
||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
||||
|
||||
put("/statuses/:id", AdminAPIController, :status_update)
|
||||
delete("/statuses/:id", AdminAPIController, :status_delete)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue