aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/config.ex4
-rw-r--r--lib/mix/tasks/pleroma/notification_settings.ex83
-rw-r--r--lib/mix/tasks/pleroma/user.ex15
-rw-r--r--lib/pleroma/activity.ex13
-rw-r--r--lib/pleroma/activity/search.ex2
-rw-r--r--lib/pleroma/application.ex25
-rw-r--r--lib/pleroma/captcha/native.ex35
-rw-r--r--lib/pleroma/config.ex12
-rw-r--r--lib/pleroma/html.ex230
-rw-r--r--lib/pleroma/moderation_log.ex42
-rw-r--r--lib/pleroma/notification.ex42
-rw-r--r--lib/pleroma/object.ex17
-rw-r--r--lib/pleroma/object/fetcher.ex2
-rw-r--r--lib/pleroma/pagination.ex93
-rw-r--r--lib/pleroma/plugs/oauth_scopes_plug.ex12
-rw-r--r--lib/pleroma/plugs/parsers_plug.ex21
-rw-r--r--lib/pleroma/plugs/user_is_admin_plug.ex27
-rw-r--r--lib/pleroma/report_note.ex48
-rw-r--r--lib/pleroma/user.ex69
-rw-r--r--lib/pleroma/user/notification_setting.ex40
-rw-r--r--lib/pleroma/utils.ex12
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex89
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex6
-rw-r--r--lib/pleroma/web/activity_pub/mrf/drop_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex59
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex2
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex12
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex10
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex70
-rw-r--r--lib/pleroma/web/admin_api/views/report_view.ex25
-rw-r--r--lib/pleroma/web/admin_api/views/status_view.ex42
-rw-r--r--lib/pleroma/web/endpoint.ex11
-rw-r--r--lib/pleroma/web/federator/federator.ex8
-rw-r--r--lib/pleroma/web/federator/publisher.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex1
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/status_controller.ex12
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex5
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex3
-rw-r--r--lib/pleroma/web/metadata/twitter_card.ex2
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex10
-rw-r--r--lib/pleroma/web/oauth/scopes.ex36
-rw-r--r--lib/pleroma/web/oauth/token/clean_worker.ex8
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex2
-rw-r--r--lib/pleroma/web/push/impl.ex27
-rw-r--r--lib/pleroma/web/router.ex6
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex3
-rw-r--r--lib/pleroma/workers/web_pusher_worker.ex2
48 files changed, 842 insertions, 459 deletions
diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex
index 0e21408b2..590c7a914 100644
--- a/lib/mix/tasks/pleroma/config.ex
+++ b/lib/mix/tasks/pleroma/config.ex
@@ -52,7 +52,9 @@ defmodule Mix.Tasks.Pleroma.Config do
|> Enum.each(fn config ->
IO.write(
file,
- "config :#{config.group}, #{config.key}, #{inspect(Config.from_binary(config.value))}\r\n\r\n"
+ "config :#{config.group}, #{config.key}, #{
+ inspect(Config.from_binary(config.value), limit: :infinity)
+ }\r\n\r\n"
)
if delete? do
diff --git a/lib/mix/tasks/pleroma/notification_settings.ex b/lib/mix/tasks/pleroma/notification_settings.ex
new file mode 100644
index 000000000..7d65f0587
--- /dev/null
+++ b/lib/mix/tasks/pleroma/notification_settings.ex
@@ -0,0 +1,83 @@
+defmodule Mix.Tasks.Pleroma.NotificationSettings do
+ @shortdoc "Enable&Disable privacy option for push notifications"
+ @moduledoc """
+ Example:
+
+ > mix pleroma.notification_settings --privacy-option=false --nickname-users="parallel588" # set false only for parallel588 user
+ > mix pleroma.notification_settings --privacy-option=true # set true for all users
+
+ """
+
+ use Mix.Task
+ import Mix.Pleroma
+ import Ecto.Query
+
+ def run(args) do
+ start_pleroma()
+
+ {options, _, _} =
+ OptionParser.parse(
+ args,
+ strict: [
+ privacy_option: :boolean,
+ email_users: :string,
+ nickname_users: :string
+ ]
+ )
+
+ privacy_option = Keyword.get(options, :privacy_option)
+
+ if not is_nil(privacy_option) do
+ privacy_option
+ |> build_query(options)
+ |> Pleroma.Repo.update_all([])
+ end
+
+ shell_info("Done")
+ end
+
+ defp build_query(privacy_option, options) do
+ query =
+ from(u in Pleroma.User,
+ update: [
+ set: [
+ notification_settings:
+ fragment(
+ "jsonb_set(notification_settings, '{privacy_option}', ?)",
+ ^privacy_option
+ )
+ ]
+ ]
+ )
+
+ user_emails =
+ options
+ |> Keyword.get(:email_users, "")
+ |> String.split(",")
+ |> Enum.map(&String.trim(&1))
+ |> Enum.reject(&(&1 == ""))
+
+ query =
+ if length(user_emails) > 0 do
+ where(query, [u], u.email in ^user_emails)
+ else
+ query
+ end
+
+ user_nicknames =
+ options
+ |> Keyword.get(:nickname_users, "")
+ |> String.split(",")
+ |> Enum.map(&String.trim(&1))
+ |> Enum.reject(&(&1 == ""))
+
+ query =
+ if length(user_nicknames) > 0 do
+ where(query, [u], u.nickname in ^user_nicknames)
+ else
+ query
+ end
+
+ query
+ end
+end
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index bc8eacda8..85c9e4954 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -8,7 +8,6 @@ defmodule Mix.Tasks.Pleroma.User do
alias Ecto.Changeset
alias Pleroma.User
alias Pleroma.UserInviteToken
- alias Pleroma.Web.OAuth
@shortdoc "Manages Pleroma users"
@moduledoc File.read!("docs/administration/CLI_tasks/user.md")
@@ -354,8 +353,7 @@ defmodule Mix.Tasks.Pleroma.User do
start_pleroma()
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
- OAuth.Token.delete_user_tokens(user)
- OAuth.Authorization.delete_user_authorizations(user)
+ User.global_sign_out(user)
shell_info("#{nickname} signed out from all apps.")
else
@@ -373,9 +371,9 @@ defmodule Mix.Tasks.Pleroma.User do
users
|> Enum.each(fn user ->
shell_info(
- "#{user.nickname} moderator: #{user.info.is_moderator}, admin: #{user.info.is_admin}, locked: #{
- user.info.locked
- }, deactivated: #{user.info.deactivated}"
+ "#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{
+ user.locked
+ }, deactivated: #{user.deactivated}"
)
end)
end)
@@ -393,10 +391,7 @@ defmodule Mix.Tasks.Pleroma.User do
end
defp set_admin(user, value) do
- {:ok, user} =
- user
- |> Changeset.change(%{is_admin: value})
- |> User.update_and_set_cache()
+ {:ok, user} = User.admin_api_update(user, %{is_admin: value})
shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
user
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 480b261cf..510d3273c 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -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,
diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex
index f847ac238..d30a5a6a5 100644
--- a/lib/pleroma/activity/search.ex
+++ b/lib/pleroma/activity/search.ex
@@ -86,7 +86,7 @@ defmodule Pleroma.Activity.Search do
{:ok, object} <- Fetcher.fetch_object_from_id(search_query),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
true <- Visibility.visible_for_user?(activity, user) do
- activities ++ [activity]
+ [activity | activities]
else
_ -> activities
end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 57462740c..2ae052069 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Application do
import Cachex.Spec
use Application
+ require Logger
@name Mix.Project.config()[:name]
@version Mix.Project.config()[:version]
@@ -30,8 +31,10 @@ defmodule Pleroma.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
+ Pleroma.HTML.compile_scrubbers()
Pleroma.Config.DeprecationWarnings.warn()
setup_instrumenters()
+ load_custom_modules()
# Define workers and child supervisors to be supervised
children =
@@ -67,6 +70,28 @@ defmodule Pleroma.Application do
Supervisor.start_link(children, opts)
end
+ def load_custom_modules do
+ dir = Pleroma.Config.get([:modules, :runtime_dir])
+
+ if dir && File.exists?(dir) do
+ dir
+ |> Pleroma.Utils.compile_dir()
+ |> case do
+ {:error, _errors, _warnings} ->
+ raise "Invalid custom modules"
+
+ {:ok, modules, _warnings} ->
+ if @env != :test do
+ Enum.each(modules, fn mod ->
+ Logger.info("Custom module loaded: #{inspect(mod)}")
+ end)
+ end
+
+ :ok
+ end
+ end
+ end
+
defp setup_instrumenters do
require Prometheus.Registry
diff --git a/lib/pleroma/captcha/native.ex b/lib/pleroma/captcha/native.ex
new file mode 100644
index 000000000..5306fe1aa
--- /dev/null
+++ b/lib/pleroma/captcha/native.ex
@@ -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
diff --git a/lib/pleroma/config.ex b/lib/pleroma/config.ex
index fcc039710..bad6d505c 100644
--- a/lib/pleroma/config.ex
+++ b/lib/pleroma/config.ex
@@ -65,4 +65,16 @@ defmodule Pleroma.Config do
def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], [])
def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
+
+ def enforce_oauth_admin_scope_usage?, do: !!get([:auth, :enforce_oauth_admin_scope_usage])
+
+ def oauth_admin_scopes(scopes) when is_list(scopes) do
+ Enum.flat_map(
+ scopes,
+ fn scope ->
+ ["admin:#{scope}"] ++
+ if enforce_oauth_admin_scope_usage?(), do: [], else: [scope]
+ end
+ )
+ end
end
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index 71c53ce0e..11513106e 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -3,6 +3,23 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTML do
+ # Scrubbers are compiled on boot so they can be configured in OTP releases
+ # @on_load :compile_scrubbers
+
+ def compile_scrubbers do
+ dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
+
+ dir
+ |> Pleroma.Utils.compile_dir()
+ |> case do
+ {:error, _errors, _warnings} ->
+ raise "Compiling scrubbers failed"
+
+ {:ok, _modules, _warnings} ->
+ :ok
+ end
+ end
+
defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
@@ -99,216 +116,3 @@ defmodule Pleroma.HTML do
end)
end
end
-
-defmodule Pleroma.HTML.Scrubber.TwitterText do
- @moduledoc """
- An HTML scrubbing policy which limits to twitter-style text. Only
- paragraphs, breaks and links are allowed through the filter.
- """
-
- @valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
-
- require FastSanitize.Sanitizer.Meta
- alias FastSanitize.Sanitizer.Meta
-
- Meta.strip_comments()
-
- # links
- Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
-
- Meta.allow_tag_with_this_attribute_values(:a, "class", [
- "hashtag",
- "u-url",
- "mention",
- "u-url mention",
- "mention u-url"
- ])
-
- Meta.allow_tag_with_this_attribute_values(:a, "rel", [
- "tag",
- "nofollow",
- "noopener",
- "noreferrer"
- ])
-
- Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
-
- # paragraphs and linebreaks
- Meta.allow_tag_with_these_attributes(:br, [])
- Meta.allow_tag_with_these_attributes(:p, [])
-
- # microformats
- Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
- Meta.allow_tag_with_these_attributes(:span, [])
-
- # allow inline images for custom emoji
- if Pleroma.Config.get([:markup, :allow_inline_images]) do
- # restrict img tags to http/https only, because of MediaProxy.
- Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
-
- Meta.allow_tag_with_these_attributes(:img, [
- "width",
- "height",
- "class",
- "title",
- "alt"
- ])
- end
-
- Meta.strip_everything_not_covered()
-end
-
-defmodule Pleroma.HTML.Scrubber.Default do
- @doc "The default HTML scrubbing policy: no "
-
- require FastSanitize.Sanitizer.Meta
- alias FastSanitize.Sanitizer.Meta
-
- # credo:disable-for-previous-line
- # No idea how to fix this one…
-
- @valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
-
- Meta.strip_comments()
-
- Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
-
- Meta.allow_tag_with_this_attribute_values(:a, "class", [
- "hashtag",
- "u-url",
- "mention",
- "u-url mention",
- "mention u-url"
- ])
-
- Meta.allow_tag_with_this_attribute_values(:a, "rel", [
- "tag",
- "nofollow",
- "noopener",
- "noreferrer",
- "ugc"
- ])
-
- Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
-
- Meta.allow_tag_with_these_attributes(:abbr, ["title"])
-
- Meta.allow_tag_with_these_attributes(:b, [])
- Meta.allow_tag_with_these_attributes(:blockquote, [])
- Meta.allow_tag_with_these_attributes(:br, [])
- Meta.allow_tag_with_these_attributes(:code, [])
- Meta.allow_tag_with_these_attributes(:del, [])
- Meta.allow_tag_with_these_attributes(:em, [])
- Meta.allow_tag_with_these_attributes(:i, [])
- Meta.allow_tag_with_these_attributes(:li, [])
- Meta.allow_tag_with_these_attributes(:ol, [])
- Meta.allow_tag_with_these_attributes(:p, [])
- Meta.allow_tag_with_these_attributes(:pre, [])
- Meta.allow_tag_with_these_attributes(:strong, [])
- Meta.allow_tag_with_these_attributes(:sub, [])
- Meta.allow_tag_with_these_attributes(:sup, [])
- Meta.allow_tag_with_these_attributes(:u, [])
- Meta.allow_tag_with_these_attributes(:ul, [])
-
- Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
- Meta.allow_tag_with_these_attributes(:span, [])
-
- @allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
-
- if @allow_inline_images do
- # restrict img tags to http/https only, because of MediaProxy.
- Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
-
- Meta.allow_tag_with_these_attributes(:img, [
- "width",
- "height",
- "class",
- "title",
- "alt"
- ])
- end
-
- if Pleroma.Config.get([:markup, :allow_tables]) do
- Meta.allow_tag_with_these_attributes(:table, [])
- Meta.allow_tag_with_these_attributes(:tbody, [])
- Meta.allow_tag_with_these_attributes(:td, [])
- Meta.allow_tag_with_these_attributes(:th, [])
- Meta.allow_tag_with_these_attributes(:thead, [])
- Meta.allow_tag_with_these_attributes(:tr, [])
- end
-
- if Pleroma.Config.get([:markup, :allow_headings]) do
- Meta.allow_tag_with_these_attributes(:h1, [])
- Meta.allow_tag_with_these_attributes(:h2, [])
- Meta.allow_tag_with_these_attributes(:h3, [])
- Meta.allow_tag_with_these_attributes(:h4, [])
- Meta.allow_tag_with_these_attributes(:h5, [])
- end
-
- if Pleroma.Config.get([:markup, :allow_fonts]) do
- Meta.allow_tag_with_these_attributes(:font, ["face"])
- end
-
- Meta.strip_everything_not_covered()
-end
-
-defmodule Pleroma.HTML.Transform.MediaProxy do
- @moduledoc "Transforms inline image URIs to use MediaProxy."
-
- alias Pleroma.Web.MediaProxy
-
- def before_scrub(html), do: html
-
- def scrub_attribute(:img, {"src", "http" <> target}) do
- media_url =
- ("http" <> target)
- |> MediaProxy.url()
-
- {"src", media_url}
- end
-
- def scrub_attribute(_tag, attribute), do: attribute
-
- def scrub({:img, attributes, children}) do
- attributes =
- attributes
- |> Enum.map(fn attr -> scrub_attribute(:img, attr) end)
- |> Enum.reject(&is_nil(&1))
-
- {:img, attributes, children}
- end
-
- def scrub({:comment, _text, _children}), do: ""
-
- def scrub({tag, attributes, children}), do: {tag, attributes, children}
- def scrub({_tag, children}), do: children
- def scrub(text), do: text
-end
-
-defmodule Pleroma.HTML.Scrubber.LinksOnly do
- @moduledoc """
- An HTML scrubbing policy which limits to links only.
- """
-
- @valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
-
- require FastSanitize.Sanitizer.Meta
- alias FastSanitize.Sanitizer.Meta
-
- Meta.strip_comments()
-
- # links
- Meta.allow_tag_with_uri_attributes(:a, ["href"], @valid_schemes)
-
- Meta.allow_tag_with_this_attribute_values(:a, "rel", [
- "tag",
- "nofollow",
- "noopener",
- "noreferrer",
- "me",
- "ugc"
- ])
-
- Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
- Meta.strip_everything_not_covered()
-end
diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex
index 706f089dc..c81477f48 100644
--- a/lib/pleroma/moderation_log.ex
+++ b/lib/pleroma/moderation_log.ex
@@ -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} 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} responded with '#{text}' to report ##{subject_id}"
+ "@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}"
end
@spec get_log_entry_message(ModerationLog) :: String.t()
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 71423ce5e..8f3e46af9 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -121,10 +121,28 @@ defmodule Pleroma.Notification do
when is_list(visibility) do
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
query
+ |> join(:left, [n, a], mutated_activity in Pleroma.Activity,
+ on:
+ fragment("?->>'context'", a.data) ==
+ fragment("?->>'context'", mutated_activity.data) and
+ fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and
+ fragment("?->>'type'", mutated_activity.data) == "Create",
+ as: :mutated_activity
+ )
|> where(
- [n, a],
+ [n, a, mutated_activity: mutated_activity],
not fragment(
- "activity_visibility(?, ?, ?) = ANY (?)",
+ """
+ CASE WHEN (?->>'type') = 'Like' or (?->>'type') = 'Announce'
+ THEN (activity_visibility(?, ?, ?) = ANY (?))
+ ELSE (activity_visibility(?, ?, ?) = ANY (?)) END
+ """,
+ a.data,
+ a.data,
+ mutated_activity.actor,
+ mutated_activity.recipients,
+ mutated_activity.data,
+ ^visibility,
a.actor,
a.recipients,
a.data,
@@ -139,17 +157,7 @@ defmodule Pleroma.Notification do
defp exclude_visibility(query, %{exclude_visibilities: visibility})
when visibility in @valid_visibilities do
- query
- |> where(
- [n, a],
- not fragment(
- "activity_visibility(?, ?, ?) = (?)",
- a.actor,
- a.recipients,
- a.data,
- ^visibility
- )
- )
+ exclude_visibility(query, [visibility])
end
defp exclude_visibility(query, %{exclude_visibilities: visibility})
@@ -347,7 +355,7 @@ defmodule Pleroma.Notification do
def skip?(
:followers,
activity,
- %{notification_settings: %{"followers" => false}} = user
+ %{notification_settings: %{followers: false}} = user
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
@@ -357,14 +365,14 @@ defmodule Pleroma.Notification do
def skip?(
:non_followers,
activity,
- %{notification_settings: %{"non_followers" => false}} = user
+ %{notification_settings: %{non_followers: false}} = user
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
!User.following?(follower, user)
end
- def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do
+ def skip?(:follows, activity, %{notification_settings: %{follows: false}} = user) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)
User.following?(user, followed)
@@ -373,7 +381,7 @@ defmodule Pleroma.Notification do
def skip?(
:non_follows,
activity,
- %{notification_settings: %{"non_follows" => false}} = user
+ %{notification_settings: %{non_follows: false}} = user
) do
actor = activity.data["actor"]
followed = User.get_cached_by_ap_id(actor)
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index ff0e59241..eb37b95a6 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -23,6 +23,23 @@ defmodule Pleroma.Object do
timestamps()
end
+ def with_joined_activity(query, activity_type \\ "Create", join_type \\ :inner) do
+ object_position = Map.get(query.aliases, :object, 0)
+
+ join(query, join_type, [{object, object_position}], a in Activity,
+ on:
+ fragment(
+ "COALESCE(?->'object'->>'id', ?->>'object') = (? ->> 'id') AND (?->>'type' = ?) ",
+ a.data,
+ a.data,
+ object.data,
+ a.data,
+ ^activity_type
+ ),
+ as: :object_activity
+ )
+ end
+
def create(data) do
Object.change(%Object{}, %{data: data})
|> Repo.insert()
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index 4d71c91a8..a1bde90f1 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -154,7 +154,7 @@ defmodule Pleroma.Object.Fetcher do
end
def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
- Logger.info("Fetching object #{id} via AP")
+ Logger.debug("Fetching object #{id} via AP")
date = Pleroma.Signature.signed_date()
diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex
index 9d279fba7..4535ca7c5 100644
--- a/lib/pleroma/pagination.ex
+++ b/lib/pleroma/pagination.ex
@@ -13,60 +13,66 @@ defmodule Pleroma.Pagination do
alias Pleroma.Repo
@default_limit 20
+ @page_keys ["max_id", "min_id", "limit", "since_id", "order"]
- def fetch_paginated(query, params, type \\ :keyset)
+ def page_keys, do: @page_keys
- def fetch_paginated(query, %{"total" => true} = params, :keyset) do
+ def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil)
+
+ def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do
total = Repo.aggregate(query, :count, :id)
%{
total: total,
- items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset)
+ items: fetch_paginated(query, Map.drop(params, ["total"]), :keyset, table_binding)
}
end
- def fetch_paginated(query, params, :keyset) do
+ def fetch_paginated(query, params, :keyset, table_binding) do
options = cast_params(params)
query
- |> paginate(options, :keyset)
+ |> paginate(options, :keyset, table_binding)
|> Repo.all()
|> enforce_order(options)
end
- def fetch_paginated(query, %{"total" => true} = params, :offset) do
- total = Repo.aggregate(query, :count, :id)
+ def fetch_paginated(query, %{"total" => true} = params, :offset, table_binding) do
+ total =
+ query
+ |> Ecto.Query.exclude(:left_join)
+ |> Repo.aggregate(:count, :id)
%{
total: total,
- items: fetch_paginated(query, Map.drop(params, ["total"]), :offset)
+ items: fetch_paginated(query, Map.drop(params, ["total"]), :offset, table_binding)
}
end
- def fetch_paginated(query, params, :offset) do
+ def fetch_paginated(query, params, :offset, table_binding) do
options = cast_params(params)
query
- |> paginate(options, :offset)
+ |> paginate(options, :offset, table_binding)
|> Repo.all()
end
- def paginate(query, options, method \\ :keyset)
+ def paginate(query, options, method \\ :keyset, table_binding \\ nil)
- def paginate(query, options, :keyset) do
+ def paginate(query, options, :keyset, table_binding) do
query
- |> restrict(:min_id, options)
- |> restrict(:since_id, options)
- |> restrict(:max_id, options)
- |> restrict(:order, options)
- |> restrict(:limit, options)
+ |> restrict(:min_id, options, table_binding)
+ |> restrict(:since_id, options, table_binding)
+ |> restrict(:max_id, options, table_binding)
+ |> restrict(:order, options, table_binding)
+ |> restrict(:limit, options, table_binding)
end
- def paginate(query, options, :offset) do
+ def paginate(query, options, :offset, table_binding) do
query
- |> restrict(:order, options)
- |> restrict(:offset, options)
- |> restrict(:limit, options)
+ |> restrict(:order, options, table_binding)
+ |> restrict(:offset, options, table_binding)
+ |> restrict(:limit, options, table_binding)
end
defp cast_params(params) do
@@ -75,7 +81,8 @@ defmodule Pleroma.Pagination do
since_id: :string,
max_id: :string,
offset: :integer,
- limit: :integer
+ limit: :integer,
+ skip_order: :boolean
}
params =
@@ -88,38 +95,48 @@ defmodule Pleroma.Pagination do
changeset.changes
end
- defp restrict(query, :min_id, %{min_id: min_id}) do
- where(query, [q], q.id > ^min_id)
+ defp restrict(query, :min_id, %{min_id: min_id}, table_binding) do
+ where(query, [{q, table_position(query, table_binding)}], q.id > ^min_id)
end
- defp restrict(query, :since_id, %{since_id: since_id}) do
- where(query, [q], q.id > ^since_id)
+ defp restrict(query, :since_id, %{since_id: since_id}, table_binding) do
+ where(query, [{q, table_position(query, table_binding)}], q.id > ^since_id)
end
- defp restrict(query, :max_id, %{max_id: max_id}) do
- where(query, [q], q.id < ^max_id)
+ defp restrict(query, :max_id, %{max_id: max_id}, table_binding) do
+ where(query, [{q, table_position(query, table_binding)}], q.id < ^max_id)
end
- defp restrict(query, :order, %{min_id: _}) do
- order_by(query, [u], fragment("? asc nulls last", u.id))
+ defp restrict(query, :order, %{skip_order: true}, _), do: query
+
+ defp restrict(query, :order, %{min_id: _}, table_binding) do
+ order_by(
+ query,
+ [{u, table_position(query, table_binding)}],
+ fragment("? asc nulls last", u.id)
+ )
end
- defp restrict(query, :order, _options) do
- order_by(query, [u], fragment("? desc nulls last", u.id))
+ defp restrict(query, :order, _options, table_binding) do
+ order_by(
+ query,
+ [{u, table_position(query, table_binding)}],
+ fragment("? desc nulls last", u.id)
+ )
end
- defp restrict(query, :offset, %{offset: offset}) do
+ defp restrict(query, :offset, %{offset: offset}, _table_binding) do
offset(query, ^offset)
end
- defp restrict(query, :limit, options) do
+ defp restrict(query, :limit, options, _table_binding) do
limit = Map.get(options, :limit, @default_limit)
query
|> limit(^limit)
end
- defp restrict(query, _, _), do: query
+ defp restrict(query, _, _, _), do: query
defp enforce_order(result, %{min_id: _}) do
result
@@ -127,4 +144,10 @@ defmodule Pleroma.Pagination do
end
defp enforce_order(result, _), do: result
+
+ defp table_position(%Ecto.Query{} = query, binding_name) do
+ Map.get(query.aliases, binding_name, 0)
+ end
+
+ defp table_position(_, _), do: 0
end
diff --git a/lib/pleroma/plugs/oauth_scopes_plug.ex b/lib/pleroma/plugs/oauth_scopes_plug.ex
index a3278dbef..174a8389c 100644
--- a/lib/pleroma/plugs/oauth_scopes_plug.ex
+++ b/lib/pleroma/plugs/oauth_scopes_plug.ex
@@ -6,6 +6,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
import Plug.Conn
import Pleroma.Web.Gettext
+ alias Pleroma.Config
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
@behaviour Plug
@@ -15,6 +16,8 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
op = options[:op] || :|
token = assigns[:token]
+
+ scopes = transform_scopes(scopes, options)
matched_scopes = token && filter_descendants(scopes, token.scopes)
cond do
@@ -60,6 +63,15 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
)
end
+ @doc "Transforms scopes by applying supported options (e.g. :admin)"
+ def transform_scopes(scopes, options) do
+ if options[:admin] do
+ Config.oauth_admin_scopes(scopes)
+ else
+ scopes
+ end
+ end
+
defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do
if options[:skip_instance_privacy_check] do
conn
diff --git a/lib/pleroma/plugs/parsers_plug.ex b/lib/pleroma/plugs/parsers_plug.ex
new file mode 100644
index 000000000..2e493ce0e
--- /dev/null
+++ b/lib/pleroma/plugs/parsers_plug.ex
@@ -0,0 +1,21 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.Parsers do
+ @moduledoc "Initializes Plug.Parsers with upload limit set at boot time"
+
+ @behaviour Plug
+
+ def init(_opts) do
+ Plug.Parsers.init(
+ parsers: [:urlencoded, :multipart, :json],
+ pass: ["*/*"],
+ json_decoder: Jason,
+ length: Pleroma.Config.get([:instance, :upload_limit]),
+ body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
+ )
+ end
+
+ defdelegate call(conn, opts), to: Plug.Parsers
+end
diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/plugs/user_is_admin_plug.ex
index ee808f31f..582fb1f92 100644
--- a/lib/pleroma/plugs/user_is_admin_plug.ex
+++ b/lib/pleroma/plugs/user_is_admin_plug.ex
@@ -5,19 +5,38 @@
defmodule Pleroma.Plugs.UserIsAdminPlug do
import Pleroma.Web.TranslationHelpers
import Plug.Conn
+
alias Pleroma.User
+ alias Pleroma.Web.OAuth
def init(options) do
options
end
- def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
- conn
+ def call(%{assigns: %{user: %User{is_admin: true}} = assigns} = conn, _) do
+ token = assigns[:token]
+
+ cond do
+ not Pleroma.Config.enforce_oauth_admin_scope_usage?() ->
+ conn
+
+ token && OAuth.Scopes.contains_admin_scopes?(token.scopes) ->
+ # Note: checking for _any_ admin scope presence, not necessarily fitting requested action.
+ # Thus, controller must explicitly invoke OAuthScopesPlug to verify scope requirements.
+ conn
+
+ true ->
+ fail(conn)
+ end
end
def call(conn, _) do
+ fail(conn)
+ end
+
+ defp fail(conn) do
conn
- |> render_error(:forbidden, "User is not admin.")
- |> halt
+ |> render_error(:forbidden, "User is not an admin or OAuth admin scope is not granted.")
+ |> halt()
end
end
diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex
new file mode 100644
index 000000000..0db86d1a1
--- /dev/null
+++ b/lib/pleroma/report_note.ex
@@ -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
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index b7f50e5ac..706aee2ff 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -127,15 +127,13 @@ defmodule Pleroma.User do
field(:invisible, :boolean, default: false)
field(:allow_following_move, :boolean, default: true)
field(:skip_thread_containment, :boolean, default: false)
+ field(:actor_type, :string, default: "Person")
field(:also_known_as, {:array, :string}, default: [])
- field(:notification_settings, :map,
- default: %{
- "followers" => true,
- "follows" => true,
- "non_follows" => true,
- "non_followers" => true
- }
+ embeds_one(
+ :notification_settings,
+ Pleroma.User.NotificationSetting,
+ on_replace: :update
)
has_many(:notifications, Notification)
@@ -349,6 +347,7 @@ defmodule Pleroma.User do
:following_count,
:discoverable,
:invisible,
+ :actor_type,
:also_known_as
]
)
@@ -399,6 +398,7 @@ defmodule Pleroma.User do
:raw_fields,
:pleroma_settings_store,
:discoverable,
+ :actor_type,
:also_known_as
]
)
@@ -441,6 +441,7 @@ defmodule Pleroma.User do
:discoverable,
:hide_followers_count,
:hide_follows_count,
+ :actor_type,
:also_known_as
]
)
@@ -861,6 +862,13 @@ defmodule Pleroma.User do
|> Repo.all()
end
+ def get_friends_ap_ids(user) do
+ user
+ |> get_friends_query(nil)
+ |> select([u], u.ap_id)
+ |> Repo.all()
+ end
+
def get_friends_ids(user, page \\ nil) do
user
|> get_friends_query(page)
@@ -1135,7 +1143,8 @@ defmodule Pleroma.User do
def blocks?(nil, _), do: false
def blocks?(%User{} = user, %User{} = target) do
- blocks_user?(user, target) || blocks_domain?(user, target)
+ blocks_user?(user, target) ||
+ (!User.following?(user, target) && blocks_domain?(user, target))
end
def blocks_user?(%User{} = user, %User{} = target) do
@@ -1221,20 +1230,9 @@ defmodule Pleroma.User do
end
def update_notification_settings(%User{} = user, settings) do
- settings =
- settings
- |> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
- |> Map.new()
-
- notification_settings =
- user.notification_settings
- |> Map.merge(settings)
- |> Map.take(["followers", "follows", "non_follows", "non_followers"])
-
- params = %{notification_settings: notification_settings}
-
user
- |> cast(params, [:notification_settings])
+ |> cast(%{notification_settings: settings}, [])
+ |> cast_embed(:notification_settings)
|> validate_required([:notification_settings])
|> update_and_set_cache()
end
@@ -1849,13 +1847,28 @@ defmodule Pleroma.User do
end
def admin_api_update(user, params) do
- user
- |> cast(params, [
- :is_moderator,
- :is_admin,
- :show_role
- ])
- |> update_and_set_cache()
+ changeset =
+ cast(user, params, [
+ :is_moderator,
+ :is_admin,
+ :show_role
+ ])
+
+ with {:ok, updated_user} <- update_and_set_cache(changeset) do
+ if user.is_admin && !updated_user.is_admin do
+ # Tokens & authorizations containing any admin scopes must be revoked (revoking all).
+ # This is an extra safety measure (tokens' admin scopes won't be accepted for non-admins).
+ global_sign_out(user)
+ end
+
+ {:ok, updated_user}
+ end
+ end
+
+ @doc "Signs user out of all applications"
+ def global_sign_out(user) do
+ OAuth.Authorization.delete_user_authorizations(user)
+ OAuth.Token.delete_user_tokens(user)
end
def mascot_update(user, url) do
diff --git a/lib/pleroma/user/notification_setting.ex b/lib/pleroma/user/notification_setting.ex
new file mode 100644
index 000000000..f0899613e
--- /dev/null
+++ b/lib/pleroma/user/notification_setting.ex
@@ -0,0 +1,40 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.NotificationSetting do
+ use Ecto.Schema
+ import Ecto.Changeset
+
+ @derive Jason.Encoder
+ @primary_key false
+
+ embedded_schema do
+ field(:followers, :boolean, default: true)
+ field(:follows, :boolean, default: true)
+ field(:non_follows, :boolean, default: true)
+ field(:non_followers, :boolean, default: true)
+ field(:privacy_option, :boolean, default: false)
+ end
+
+ def changeset(schema, params) do
+ schema
+ |> cast(prepare_attrs(params), [
+ :followers,
+ :follows,
+ :non_follows,
+ :non_followers,
+ :privacy_option
+ ])
+ end
+
+ defp prepare_attrs(params) do
+ Enum.reduce(params, %{}, fn
+ {k, v}, acc when is_binary(v) ->
+ Map.put(acc, k, String.downcase(v))
+
+ {k, v}, acc ->
+ Map.put(acc, k, v)
+ end)
+ end
+end
diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex
new file mode 100644
index 000000000..8d36a0001
--- /dev/null
+++ b/lib/pleroma/utils.ex
@@ -0,0 +1,12 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Utils do
+ def compile_dir(dir) when is_binary(dir) do
+ dir
+ |> File.ls!()
+ |> Enum.map(&Path.join(dir, &1))
+ |> Kernel.ParallelCompiler.compile()
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 1e2cc2e2b..60c9e7e64 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -950,6 +950,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
domain_blocks = user.domain_blocks || []
+ following_ap_ids = User.get_friends_ap_ids(user)
+
query =
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
@@ -964,8 +966,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
activity.data,
^blocked_ap_ids
),
- where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks),
- where: fragment("not (split_part(?->>'actor', '/', 3) = ANY(?))", o.data, ^domain_blocks)
+ where:
+ fragment(
+ "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
+ activity.actor,
+ ^domain_blocks,
+ activity.actor,
+ ^following_ap_ids
+ ),
+ where:
+ fragment(
+ "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
+ o.data,
+ ^domain_blocks,
+ o.data,
+ ^following_ap_ids
+ )
)
end
@@ -1052,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
@@ -1105,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"])
@@ -1141,6 +1165,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_update_cc(list_memberships, opts["user"])
end
+ @doc """
+ Fetch favorites activities of user with order by sort adds to favorites
+ """
+ @spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
+ def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
+ user.ap_id
+ |> Activity.Queries.by_actor()
+ |> Activity.Queries.by_type("Like")
+ |> Activity.with_joined_object()
+ |> Object.with_joined_activity()
+ |> select([_like, object, activity], %{activity | object: object})
+ |> order_by([like, _, _], desc: like.id)
+ |> Pagination.fetch_paginated(
+ Map.merge(params, %{"skip_order" => true}),
+ pagination,
+ :object_activity
+ )
+ end
+
defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
when is_list(list_memberships) and length(list_memberships) > 0 do
Enum.map(activities, fn
@@ -1217,6 +1260,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
data = Transmogrifier.maybe_fix_user_object(data)
discoverable = data["discoverable"] || false
invisible = data["invisible"] || false
+ actor_type = data["type"] || "Person"
user_data = %{
ap_id: data["id"],
@@ -1232,6 +1276,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
follower_address: data["followers"],
following_address: data["following"],
bio: data["summary"],
+ actor_type: actor_type,
also_known_as: Map.get(data, "alsoKnownAs", [])
}
@@ -1253,28 +1298,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_follow_information_for_user(user) do
with {:ok, following_data} <-
Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
- following_count when is_integer(following_count) <- following_data["totalItems"],
{:ok, hide_follows} <- collection_private(following_data),
{:ok, followers_data} <-
Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
- followers_count when is_integer(followers_count) <- followers_data["totalItems"],
{:ok, hide_followers} <- collection_private(followers_data) do
{:ok,
%{
hide_follows: hide_follows,
- follower_count: followers_count,
- following_count: following_count,
+ follower_count: normalize_counter(followers_data["totalItems"]),
+ following_count: normalize_counter(following_data["totalItems"]),
hide_followers: hide_followers
}}
else
- {:error, _} = e ->
- e
-
- e ->
- {:error, e}
+ {:error, _} = e -> e
+ e -> {:error, e}
end
end
+ defp normalize_counter(counter) when is_integer(counter), do: counter
+ defp normalize_counter(_), do: 0
+
defp maybe_update_follow_information(data) do
with {:enabled, true} <-
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
@@ -1294,24 +1337,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp collection_private(%{"first" => %{"type" => type}})
+ when type in ["CollectionPage", "OrderedCollectionPage"],
+ do: {:ok, false}
+
defp collection_private(%{"first" => first}) do
- if is_map(first) and
- first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
+ with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
+ Fetcher.fetch_and_contain_remote_object_from_id(first) do
{:ok, false}
else
- with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
- Fetcher.fetch_and_contain_remote_object_from_id(first) do
- {:ok, false}
- else
- {:error, {:ok, %{status: code}}} when code in [401, 403] ->
- {:ok, true}
-
- {:error, _} = e ->
- e
-
- e ->
- {:error, e}
- end
+ {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
+ {:error, _} = e -> e
+ e -> {:error, e}
end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index dec5da0d3..5059e3984 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -257,7 +257,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
# only accept relayed Creates
def inbox(conn, %{"type" => "Create"} = params) do
- Logger.info(
+ Logger.debug(
"Signature missing or not from author, relayed Create message, fetching object from source"
)
@@ -270,11 +270,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
headers = Enum.into(conn.req_headers, %{})
if String.contains?(headers["signature"], params["actor"]) do
- Logger.info(
+ Logger.debug(
"Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
)
- Logger.info(inspect(conn.req_headers))
+ Logger.debug(inspect(conn.req_headers))
end
json(conn, dgettext("errors", "error"))
diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
index f7831bc3e..4a5709974 100644
--- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
@impl true
def filter(object) do
- Logger.info("REJECTING #{inspect(object)}")
+ Logger.debug("REJECTING #{inspect(object)}")
{:reject, object}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex
index 26b8539fe..df774b0f7 100644
--- a/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex
@@ -18,7 +18,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
]
def perform(:prefetch, url) do
- Logger.info("Prefetching #{inspect(url)}")
+ Logger.debug("Prefetching #{inspect(url)}")
url
|> MediaProxy.url()
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index 4ea37fc7b..db072bad2 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -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
@@ -47,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
* `id`: the ActivityStreams URI of the message
"""
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
- Logger.info("Federating #{id} to #{inbox}")
+ Logger.debug("Federating #{id} to #{inbox}")
%{host: host, path: path} = URI.parse(inbox)
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
@@ -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)
-
- # 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)
-
- 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
- })
+ 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()
+
+ 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)
+
+ # 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)
+
+ 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
@@ -223,7 +228,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
public = is_public?(activity)
if public && Config.get([:instance, :allow_relay]) do
- Logger.info(fn -> "Relaying #{activity.data["id"]} out" end)
+ Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end)
Relay.publish(activity)
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index ecba27bef..3fa789d53 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -397,7 +397,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
options
)
- when objtype in ["Article", "Note", "Video", "Page", "Question", "Answer"] do
+ when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer"] do
actor = Containment.get_actor(data)
data =
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 2ca805c09..db7084246 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -22,7 +22,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do
require Logger
require Pleroma.Constants
- @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer", "Audio"]
+ @supported_object_types [
+ "Article",
+ "Note",
+ "Event",
+ "Video",
+ "Page",
+ "Question",
+ "Answer",
+ "Audio"
+ ]
@strip_status_report_states ~w(closed resolved)
@supported_report_states ~w(open closed resolved)
@valid_visibilities ~w(public unlisted private direct)
@@ -787,6 +796,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)
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index cf08045c9..350c4391d 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -91,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
%{
"id" => user.ap_id,
- "type" => "Person",
+ "type" => user.actor_type,
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
@@ -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)
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index b003d1f35..c8abeff06 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -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
@@ -30,13 +31,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug(
OAuthScopesPlug,
- %{scopes: ["read:accounts"]}
+ %{scopes: ["read:accounts"], admin: true}
when action in [:list_users, :user_show, :right_get, :invites]
)
plug(
OAuthScopesPlug,
- %{scopes: ["write:accounts"]}
+ %{scopes: ["write:accounts"], admin: true}
when action in [
:get_invite_token,
:revoke_invite,
@@ -58,35 +59,37 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug(
OAuthScopesPlug,
- %{scopes: ["read:reports"]} when action in [:list_reports, :report_show]
+ %{scopes: ["read:reports"], admin: true}
+ when action in [:list_reports, :report_show]
)
plug(
OAuthScopesPlug,
- %{scopes: ["write:reports"]}
+ %{scopes: ["write:reports"], admin: true}
when action in [:report_update_state, :report_respond]
)
plug(
OAuthScopesPlug,
- %{scopes: ["read:statuses"]} when action == :list_user_statuses
+ %{scopes: ["read:statuses"], admin: true}
+ when action == :list_user_statuses
)
plug(
OAuthScopesPlug,
- %{scopes: ["write:statuses"]}
+ %{scopes: ["write:statuses"], admin: true}
when action in [:status_update, :status_delete]
)
plug(
OAuthScopesPlug,
- %{scopes: ["read"]}
+ %{scopes: ["read"], admin: true}
when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log]
)
plug(
OAuthScopesPlug,
- %{scopes: ["write"]}
+ %{scopes: ["write"], admin: true}
when action in [:relay_follow, :relay_unfollow, :config_update]
)
@@ -238,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
@@ -641,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
@@ -687,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")
+ 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_note",
+ actor: user,
+ subject: Activity.get_by_id(report_id),
+ text: content
+ })
- {:ok, activity} = CommonAPI.post(user, params)
+ json_response(conn, :no_content, "")
+ else
+ _ -> json_response(conn, :bad_request, "")
+ end
+ end
+ 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_response",
+ action: "report_note_delete",
actor: user,
- subject: activity,
- text: params["status"]
+ subject: Activity.get_by_id(report_id),
+ text: note.content
})
- conn
- |> put_view(StatusView)
- |> render("show.json", %{activity: activity})
+ json_response(conn, :no_content, "")
else
- true ->
- {:param_cast, nil}
-
- nil ->
- {:error, :not_found}
+ _ -> json_response(conn, :bad_request, "")
end
end
diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
index 13602efd9..4880d2992 100644
--- a/lib/pleroma/web/admin_api/views/report_view.ex
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -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}))
diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex
new file mode 100644
index 000000000..6f2b2b09c
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/status_view.ex
@@ -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
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 49735b5c2..d32c38a05 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -59,16 +59,9 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Plugs.TrailingFormatPlug)
plug(Plug.RequestId)
- plug(Plug.Logger)
+ plug(Plug.Logger, log: :debug)
- plug(
- Plug.Parsers,
- parsers: [:urlencoded, :multipart, :json],
- pass: ["*/*"],
- json_decoder: Jason,
- length: Pleroma.Config.get([:instance, :upload_limit]),
- body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
- )
+ plug(Pleroma.Plugs.Parsers)
plug(Plug.MethodOverride)
plug(Plug.Head)
diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex
index e8a56ebd7..f506a7d24 100644
--- a/lib/pleroma/web/federator/federator.ex
+++ b/lib/pleroma/web/federator/federator.ex
@@ -58,7 +58,7 @@ defmodule Pleroma.Web.Federator do
end
def perform(:incoming_ap_doc, params) do
- Logger.info("Handling incoming AP activity")
+ Logger.debug("Handling incoming AP activity")
params = Utils.normalize_params(params)
@@ -71,13 +71,13 @@ defmodule Pleroma.Web.Federator do
{:ok, activity}
else
%Activity{} ->
- Logger.info("Already had #{params["id"]}")
+ Logger.debug("Already had #{params["id"]}")
:error
_e ->
# Just drop those for now
- Logger.info("Unhandled activity")
- Logger.info(Jason.encode!(params, pretty: true))
+ Logger.debug("Unhandled activity")
+ Logger.debug(Jason.encode!(params, pretty: true))
:error
end
end
diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex
index fb9b26649..1d045c644 100644
--- a/lib/pleroma/web/federator/publisher.ex
+++ b/lib/pleroma/web/federator/publisher.ex
@@ -47,7 +47,7 @@ defmodule Pleroma.Web.Federator.Publisher do
Config.get([:instance, :federation_publisher_modules])
|> Enum.each(fn module ->
if module.is_representable?(activity) do
- Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}")
+ Logger.debug("Publishing #{activity.data["id"]} using #{inspect(module)}")
module.publish(user, activity)
end
end)
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index d19029cb5..38d14256f 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -188,6 +188,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
{:ok, Map.merge(user.pleroma_settings_store, value)}
end)
|> add_if_present(params, "default_scope", :default_scope)
+ |> add_if_present(params, "actor_type", :actor_type)
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
index 74b223cf4..1149fb469 100644
--- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -346,15 +346,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
@doc "GET /api/v1/favourites"
def favourites(%{assigns: %{user: user}} = conn, params) do
- params =
- params
- |> Map.put("type", "Create")
- |> Map.put("favorited_by", user.ap_id)
- |> Map.put("blocking_user", user)
-
activities =
- ActivityPub.fetch_activities([], params)
- |> Enum.reverse()
+ ActivityPub.fetch_favourites(
+ user,
+ Map.take(params, Pleroma.Pagination.page_keys())
+ )
conn
|> add_link_headers(activities)
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 546cc0ed5..a5420f480 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
0
end
- bot = (user.source_data["type"] || "Person") in ["Application", "Service"]
+ bot = user.actor_type in ["Application", "Service"]
emojis =
(user.source_data["tag"] || [])
@@ -137,7 +137,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
sensitive: false,
fields: user.raw_fields,
pleroma: %{
- discoverable: user.discoverable
+ discoverable: user.discoverable,
+ actor_type: user.actor_type
}
},
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index a0257dfa6..e9590224b 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -421,7 +421,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
end
- def render_content(%{data: %{"type" => "Video"}} = object) do
+ def render_content(%{data: %{"type" => object_type}} = object)
+ when object_type in ["Video", "Event"] do
with name when not is_nil(name) and name != "" <- object.data["name"] do
"<p><a href=\"#{object.data["id"]}\">#{name}</a></p>#{object.data["content"]}"
else
diff --git a/lib/pleroma/web/metadata/twitter_card.ex b/lib/pleroma/web/metadata/twitter_card.ex
index d6a6049b3..67419a666 100644
--- a/lib/pleroma/web/metadata/twitter_card.ex
+++ b/lib/pleroma/web/metadata/twitter_card.ex
@@ -31,7 +31,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
if attachments == [] or Metadata.activity_nsfw?(object) do
[
image_tag(user),
- {:meta, [property: "twitter:card", content: "summary_large_image"], []}
+ {:meta, [property: "twitter:card", content: "summary"], []}
]
else
attachments
diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex
index 2aee8cab2..87acdec97 100644
--- a/lib/pleroma/web/oauth/oauth_controller.ex
+++ b/lib/pleroma/web/oauth/oauth_controller.ex
@@ -222,7 +222,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
{:user_active, true} <- {:user_active, !user.deactivated},
{:password_reset_pending, false} <-
{:password_reset_pending, user.password_reset_pending},
- {:ok, scopes} <- validate_scopes(app, params),
+ {:ok, scopes} <- validate_scopes(app, params, user),
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
{:ok, token} <- Token.exchange_token(app, auth) do
json(conn, Token.Response.build(user, token))
@@ -471,7 +471,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
%App{} = app <- Repo.get_by(App, client_id: client_id),
true <- redirect_uri in String.split(app.redirect_uris),
- {:ok, scopes} <- validate_scopes(app, auth_attrs),
+ {:ok, scopes} <- validate_scopes(app, auth_attrs, user),
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
Authorization.create_authorization(app, user, scopes)
end
@@ -487,12 +487,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
do: put_session(conn, :registration_id, registration_id)
- @spec validate_scopes(App.t(), map()) ::
+ @spec validate_scopes(App.t(), map(), User.t()) ::
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
- defp validate_scopes(app, params) do
+ defp validate_scopes(%App{} = app, params, %User{} = user) do
params
|> Scopes.fetch_scopes(app.scopes)
- |> Scopes.validate(app.scopes)
+ |> Scopes.validate(app.scopes, user)
end
def default_redirect_uri(%App{} = app) do
diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex
index 48bd14407..00da225b9 100644
--- a/lib/pleroma/web/oauth/scopes.ex
+++ b/lib/pleroma/web/oauth/scopes.ex
@@ -7,6 +7,9 @@ defmodule Pleroma.Web.OAuth.Scopes do
Functions for dealing with scopes.
"""
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.User
+
@doc """
Fetch scopes from request params.
@@ -53,15 +56,38 @@ defmodule Pleroma.Web.OAuth.Scopes do
@doc """
Validates scopes.
"""
- @spec validate(list() | nil, list()) ::
+ @spec validate(list() | nil, list(), User.t()) ::
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
- def validate([], _app_scopes), do: {:error, :missing_scopes}
- def validate(nil, _app_scopes), do: {:error, :missing_scopes}
+ def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []],
+ do: {:error, :missing_scopes}
- def validate(scopes, app_scopes) do
- case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
+ def validate(scopes, app_scopes, %User{} = user) do
+ with {:ok, _} <- ensure_scopes_support(scopes, app_scopes),
+ {:ok, scopes} <- authorize_admin_scopes(scopes, app_scopes, user) do
+ {:ok, scopes}
+ end
+ end
+
+ defp ensure_scopes_support(scopes, app_scopes) do
+ case OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
^scopes -> {:ok, scopes}
_ -> {:error, :unsupported_scopes}
end
end
+
+ defp authorize_admin_scopes(scopes, app_scopes, %User{} = user) do
+ if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do
+ {:ok, scopes}
+ else
+ # 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
+
+ def contains_admin_scopes?(scopes) do
+ scopes
+ |> OAuthScopesPlug.filter_descendants(["admin"])
+ |> Enum.any?()
+ end
end
diff --git a/lib/pleroma/web/oauth/token/clean_worker.ex b/lib/pleroma/web/oauth/token/clean_worker.ex
index f639f9c6f..3c9c580d5 100644
--- a/lib/pleroma/web/oauth/token/clean_worker.ex
+++ b/lib/pleroma/web/oauth/token/clean_worker.ex
@@ -11,11 +11,6 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
@ten_seconds 10_000
@one_day 86_400_000
- @interval Pleroma.Config.get(
- [:oauth2, :clean_expired_tokens_interval],
- @one_day
- )
-
alias Pleroma.Web.OAuth.Token
alias Pleroma.Workers.BackgroundWorker
@@ -29,8 +24,9 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
@doc false
def handle_info(:perform, state) do
BackgroundWorker.enqueue("clean_expired_tokens", %{})
+ interval = Pleroma.Config.get([:oauth2, :clean_expired_tokens_interval], @one_day)
- Process.send_after(self(), :perform, @interval)
+ Process.send_after(self(), :perform, interval)
{:noreply, state}
end
diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex
index a474d41d4..69dfa92e3 100644
--- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
plug(
OAuthScopesPlug,
- %{scopes: ["write"]}
+ %{scopes: ["write"], admin: true}
when action in [
:create,
:delete,
diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex
index a6a924d02..34ec1d8d9 100644
--- a/lib/pleroma/web/push/impl.ex
+++ b/lib/pleroma/web/push/impl.ex
@@ -22,8 +22,8 @@ defmodule Pleroma.Web.Push.Impl do
@spec perform(Notification.t()) :: list(any) | :error
def perform(
%{
- activity: %{data: %{"type" => activity_type}, id: activity_id} = activity,
- user_id: user_id
+ activity: %{data: %{"type" => activity_type}} = activity,
+ user: %User{id: user_id}
} = notif
)
when activity_type in @types do
@@ -39,18 +39,17 @@ defmodule Pleroma.Web.Push.Impl do
for subscription <- fetch_subsriptions(user_id),
get_in(subscription.data, ["alerts", type]) do
%{
- title: format_title(notif),
access_token: subscription.token.token,
- body: format_body(notif, actor, object),
notification_id: notif.id,
notification_type: type,
icon: avatar_url,
preferred_locale: "en",
pleroma: %{
- activity_id: activity_id,
+ activity_id: notif.activity.id,
direct_conversation_id: direct_conversation_id
}
}
+ |> Map.merge(build_content(notif, actor, object))
|> Jason.encode!()
|> push_message(build_sub(subscription), gcm_api_key, subscription)
end
@@ -100,6 +99,24 @@ defmodule Pleroma.Web.Push.Impl do
}
end
+ def build_content(
+ %{
+ activity: %{data: %{"directMessage" => true}},
+ user: %{notification_settings: %{privacy_option: true}}
+ },
+ actor,
+ _
+ ) do
+ %{title: "New Direct Message", body: "@#{actor.nickname}"}
+ end
+
+ def build_content(notif, actor, object) do
+ %{
+ title: format_title(notif),
+ body: format_body(notif, actor, object)
+ }
+ end
+
def format_body(
%{activity: %{data: %{"type" => "Create"}}},
actor,
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index e6c4f6f14..f6c128283 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -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)
@@ -528,7 +529,10 @@ defmodule Pleroma.Web.Router do
get("/users/:nickname/feed", Feed.FeedController, :feed)
get("/users/:nickname", Feed.FeedController, :feed_redirect)
+ end
+ scope "/", Pleroma.Web do
+ pipe_through(:browser)
get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)
end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 2305bb413..799dd17ae 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -104,7 +104,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
defp is_status?(acct) do
case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do
- {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
+ {:ok, %{"type" => type}}
+ when type in ["Article", "Event", "Note", "Video", "Page", "Question"] ->
true
_ ->
diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex
index 61b451e3e..a978c4013 100644
--- a/lib/pleroma/workers/web_pusher_worker.ex
+++ b/lib/pleroma/workers/web_pusher_worker.ex
@@ -13,7 +13,7 @@ defmodule Pleroma.Workers.WebPusherWorker do
notification =
Notification
|> Repo.get(notification_id)
- |> Repo.preload([:activity])
+ |> Repo.preload([:activity, :user])
Pleroma.Web.Push.Impl.perform(notification)
end