Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into pleroma-double_mentions
This commit is contained in:
commit
55dd8ef1c7
59 changed files with 400 additions and 238 deletions
|
|
@ -0,0 +1,23 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri do
|
||||
use Ecto.Type
|
||||
|
||||
def type, do: :string
|
||||
|
||||
def cast(uri) when is_binary(uri) do
|
||||
case URI.parse(uri) do
|
||||
%URI{scheme: nil} -> :error
|
||||
%URI{} -> {:ok, uri}
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def cast(_), do: :error
|
||||
|
||||
def dump(data), do: {:ok, data}
|
||||
|
||||
def load(data), do: {:ok, data}
|
||||
end
|
||||
|
|
@ -38,9 +38,9 @@ defmodule Pleroma.Upload.Filter do
|
|||
{:ok, :noop} ->
|
||||
filter(rest, upload)
|
||||
|
||||
error ->
|
||||
Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(error)}")
|
||||
error
|
||||
{:error, e} ->
|
||||
Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(e)}")
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
20
lib/pleroma/upload/filter/only_media.ex
Normal file
20
lib/pleroma/upload/filter/only_media.ex
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.OnlyMedia do
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
alias Pleroma.Upload
|
||||
|
||||
def filter(%Upload{content_type: content_type}) do
|
||||
[type, _subtype] = String.split(content_type, "/")
|
||||
|
||||
if type in ["image", "video", "audio"] do
|
||||
{:ok, :noop}
|
||||
else
|
||||
{:error, "Disallowed content-type: #{content_type}"}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
end
|
||||
|
|
@ -124,7 +124,6 @@ defmodule Pleroma.User do
|
|||
field(:domain_blocks, {:array, :string}, default: [])
|
||||
field(:is_active, :boolean, default: true)
|
||||
field(:no_rich_text, :boolean, default: false)
|
||||
field(:ap_enabled, :boolean, default: false)
|
||||
field(:is_moderator, :boolean, default: false)
|
||||
field(:is_admin, :boolean, default: false)
|
||||
field(:show_role, :boolean, default: true)
|
||||
|
|
@ -488,7 +487,6 @@ defmodule Pleroma.User do
|
|||
:nickname,
|
||||
:public_key,
|
||||
:avatar,
|
||||
:ap_enabled,
|
||||
:banner,
|
||||
:is_locked,
|
||||
:last_refreshed_at,
|
||||
|
|
@ -1061,11 +1059,7 @@ defmodule Pleroma.User do
|
|||
end
|
||||
|
||||
def maybe_direct_follow(%User{} = follower, %User{} = followed) do
|
||||
if not ap_enabled?(followed) do
|
||||
follow(follower, followed)
|
||||
else
|
||||
{:ok, follower, followed}
|
||||
end
|
||||
{:ok, follower, followed}
|
||||
end
|
||||
|
||||
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
|
||||
|
|
@ -1898,7 +1892,6 @@ defmodule Pleroma.User do
|
|||
confirmation_token: nil,
|
||||
domain_blocks: [],
|
||||
is_active: false,
|
||||
ap_enabled: false,
|
||||
is_moderator: false,
|
||||
is_admin: false,
|
||||
mascot: nil,
|
||||
|
|
@ -2151,10 +2144,6 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def ap_enabled?(%User{local: true}), do: true
|
||||
def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
|
||||
def ap_enabled?(_), do: false
|
||||
|
||||
@doc "Gets or fetch a user by uri or nickname."
|
||||
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||
def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
|
||||
|
|
|
|||
|
|
@ -1547,7 +1547,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
%{
|
||||
ap_id: data["id"],
|
||||
uri: get_actor_url(data["url"]),
|
||||
ap_enabled: true,
|
||||
banner: normalize_image(data["image"]),
|
||||
fields: fields,
|
||||
emoji: emojis,
|
||||
|
|
@ -1668,7 +1667,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def fetch_and_prepare_user_from_ap_id(ap_id, additional \\ []) do
|
||||
defp fetch_and_prepare_user_from_ap_id(ap_id, additional) do
|
||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
|
||||
{:ok, data} <- user_data_from_user_object(data, additional) do
|
||||
{:ok, maybe_update_follow_information(data)}
|
||||
|
|
@ -1751,24 +1750,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def make_user_from_ap_id(ap_id, additional \\ []) do
|
||||
user = User.get_cached_by_ap_id(ap_id)
|
||||
|
||||
if user && !User.ap_enabled?(user) do
|
||||
Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
||||
else
|
||||
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do
|
||||
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
|
||||
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do
|
||||
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
|
||||
|
||||
if user do
|
||||
user
|
||||
|> User.remote_user_changeset(data)
|
||||
|> User.update_and_set_cache()
|
||||
else
|
||||
maybe_handle_clashing_nickname(data)
|
||||
if user do
|
||||
user
|
||||
|> User.remote_user_changeset(data)
|
||||
|> User.update_and_set_cache()
|
||||
else
|
||||
maybe_handle_clashing_nickname(data)
|
||||
|
||||
data
|
||||
|> User.remote_user_changeset()
|
||||
|> Repo.insert()
|
||||
|> User.set_cache()
|
||||
end
|
||||
data
|
||||
|> User.remote_user_changeset()
|
||||
|> Repo.insert()
|
||||
|> User.set_cache()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do
|
|||
end
|
||||
|
||||
defp maybe_refetch_user(%User{ap_id: ap_id}) do
|
||||
Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
||||
# Maybe it could use User.get_or_fetch_by_ap_id to avoid refreshing too often
|
||||
User.fetch_by_ap_id(ap_id)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
|
|||
field(:like_count, :integer, default: 0)
|
||||
field(:announcement_count, :integer, default: 0)
|
||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||
field(:url, ObjectValidators.Uri)
|
||||
field(:url, ObjectValidators.BareUri)
|
||||
|
||||
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
|
||||
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
|
||||
|
|
|
|||
|
|
@ -199,7 +199,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
|
||||
inboxes =
|
||||
recipients
|
||||
|> Enum.filter(&User.ap_enabled?/1)
|
||||
|> Enum.map(fn actor -> actor.inbox end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|
|
@ -241,7 +240,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
json = Jason.encode!(data)
|
||||
|
||||
recipients(actor, activity)
|
||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||
|> Enum.map(fn %User{} = user ->
|
||||
determine_inbox(activity, user)
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Workers.TransmogrifierWorker
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
|
|
@ -946,47 +945,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|
||||
defp strip_internal_tags(object), do: object
|
||||
|
||||
def perform(:user_upgrade, user) do
|
||||
# we pass a fake user so that the followers collection is stripped away
|
||||
old_follower_address = User.ap_followers(%User{nickname: user.nickname})
|
||||
|
||||
from(
|
||||
a in Activity,
|
||||
where: ^old_follower_address in a.recipients,
|
||||
update: [
|
||||
set: [
|
||||
recipients:
|
||||
fragment(
|
||||
"array_replace(?,?,?)",
|
||||
a.recipients,
|
||||
^old_follower_address,
|
||||
^user.follower_address
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([])
|
||||
end
|
||||
|
||||
def upgrade_user_from_ap_id(ap_id) do
|
||||
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
|
||||
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
|
||||
{:ok, user} <- update_user(user, data) do
|
||||
{:ok, _pid} = Task.start(fn -> ActivityPub.pinned_fetch_task(user) end)
|
||||
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
|
||||
{:ok, user}
|
||||
else
|
||||
%User{} = user -> {:ok, user}
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
defp update_user(user, data) do
|
||||
user
|
||||
|> User.remote_user_changeset(data)
|
||||
|> User.update_and_set_cache()
|
||||
end
|
||||
|
||||
def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
|
||||
Map.put(data, "url", url["href"])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.Federator do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Federator.Publisher
|
||||
|
|
@ -80,7 +79,7 @@ defmodule Pleroma.Web.Federator do
|
|||
|
||||
# NOTE: we use the actor ID to do the containment, this is fine because an
|
||||
# actor shouldn't be acting on objects outside their own AP server.
|
||||
with {_, {:ok, _user}} <- {:actor, ap_enabled_actor(actor)},
|
||||
with {_, {:ok, _user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
|
||||
nil <- Activity.normalize(params["id"]),
|
||||
{_, :ok} <-
|
||||
{:correct_origin?, Containment.contain_origin_from_id(actor, params)},
|
||||
|
|
@ -110,14 +109,4 @@ defmodule Pleroma.Web.Federator do
|
|||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def ap_enabled_actor(id) do
|
||||
user = User.get_cached_by_ap_id(id)
|
||||
|
||||
if User.ap_enabled?(user) do
|
||||
{:ok, user}
|
||||
else
|
||||
ActivityPub.make_user_from_ap_id(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
|||
alias Pleroma.Web.MediaProxy
|
||||
alias Plug.Conn
|
||||
|
||||
plug(:sandbox)
|
||||
|
||||
def remote(conn, %{"sig" => sig64, "url" => url64}) do
|
||||
with {_, true} <- {:enabled, MediaProxy.enabled?()},
|
||||
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
||||
|
|
@ -202,4 +204,9 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
|||
defp media_proxy_opts do
|
||||
Config.get([:media_proxy, :proxy_opts], [])
|
||||
end
|
||||
|
||||
defp sandbox(conn, _params) do
|
||||
conn
|
||||
|> merge_resp_headers([{"content-security-policy", "sandbox;"}])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -76,9 +76,10 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
{:meta, [name: "twitter:card", content: "summary_large_image"], []},
|
||||
{:meta,
|
||||
[
|
||||
name: "twitter:player",
|
||||
name: "twitter:image",
|
||||
content: MediaProxy.url(url["href"])
|
||||
], []}
|
||||
], []},
|
||||
{:meta, [name: "twitter:image:alt", content: truncate(attachment["name"])], []}
|
||||
| acc
|
||||
]
|
||||
|> maybe_add_dimensions(url)
|
||||
|
|
@ -130,4 +131,12 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
metadata
|
||||
end
|
||||
end
|
||||
|
||||
defp truncate(nil), do: ""
|
||||
|
||||
defp truncate(text) do
|
||||
# truncate to 420 characters
|
||||
# see https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
|
||||
Pleroma.Formatter.truncate(text, 420)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,12 +46,32 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
|||
|
||||
config = Pleroma.Config.get(Pleroma.Upload)
|
||||
|
||||
with uploader <- Keyword.fetch!(config, :uploader),
|
||||
%{scheme: media_scheme, host: media_host, port: media_port} =
|
||||
Pleroma.Upload.base_url() |> URI.parse()
|
||||
|
||||
with {:valid_host, true} <- {:valid_host, match?(^media_host, conn.host)},
|
||||
uploader <- Keyword.fetch!(config, :uploader),
|
||||
proxy_remote = Keyword.get(config, :proxy_remote, false),
|
||||
{:ok, get_method} <- uploader.get_file(file),
|
||||
false <- media_is_banned(conn, get_method) do
|
||||
get_media(conn, get_method, proxy_remote, opts)
|
||||
else
|
||||
{:valid_host, false} ->
|
||||
redirect_url =
|
||||
%URI{
|
||||
scheme: media_scheme,
|
||||
host: media_host,
|
||||
port: media_port,
|
||||
path: conn.request_path,
|
||||
query: conn.query_string
|
||||
}
|
||||
|> URI.to_string()
|
||||
|> String.trim_trailing("?")
|
||||
|
||||
conn
|
||||
|> Phoenix.Controller.redirect(external: redirect_url)
|
||||
|> halt()
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> send_resp(:internal_server_error, dgettext("errors", "Failed"))
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.Preload do
|
|||
terms =
|
||||
params
|
||||
|> parser.generate_terms()
|
||||
|> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v))} end)
|
||||
|> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v, escape: :html_safe))} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
Map.merge(acc, terms)
|
||||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.Preload do
|
|||
|
||||
rendered_html =
|
||||
preload_data
|
||||
|> Jason.encode!()
|
||||
|> Jason.encode!(escape: :html_safe)
|
||||
|> build_script_tag()
|
||||
|> HTML.safe_to_string()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
|
|||
def parse(html, _data) do
|
||||
with elements = [_ | _] <- get_discovery_data(html),
|
||||
oembed_url when is_binary(oembed_url) <- get_oembed_url(elements),
|
||||
{:ok, oembed_data} <- get_oembed_data(oembed_url) do
|
||||
oembed_data
|
||||
{:ok, oembed_data = %{"html" => html}} <- get_oembed_data(oembed_url) do
|
||||
%{oembed_data | "html" => Pleroma.HTML.filter_tags(html)}
|
||||
else
|
||||
_e -> %{}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,7 +25,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
|||
true <- Visibility.is_public?(activity.object),
|
||||
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||
url = Helpers.url(conn) <> conn.request_path
|
||||
|
||||
meta =
|
||||
Metadata.build_tags(%{
|
||||
activity_id: notice_id,
|
||||
object: activity.object,
|
||||
user: user,
|
||||
url: url
|
||||
})
|
||||
|
||||
timeline =
|
||||
activity.object.data["context"]
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ defmodule Pleroma.Web.Streamer do
|
|||
def registry, do: @registry
|
||||
|
||||
@public_streams ["public", "public:local", "public:media", "public:local:media"]
|
||||
@local_streams ["public:local", "public:local:media"]
|
||||
@user_streams ["user", "user:notification", "direct", "user:pleroma_chat"]
|
||||
|
||||
@doc "Expands and authorizes a stream, and registers the process for streaming."
|
||||
|
|
@ -41,14 +42,37 @@ defmodule Pleroma.Web.Streamer do
|
|||
end
|
||||
end
|
||||
|
||||
defp can_access_stream(user, oauth_token, kind) do
|
||||
with {_, true} <- {:restrict?, Config.restrict_unauthenticated_access?(:timelines, kind)},
|
||||
{_, %User{id: user_id}, %Token{user_id: user_id}} <- {:user, user, oauth_token},
|
||||
{_, true} <-
|
||||
{:scopes,
|
||||
OAuthScopesPlug.filter_descendants(["read:statuses"], oauth_token.scopes) != []} do
|
||||
true
|
||||
else
|
||||
{:restrict?, _} ->
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Expand and authorizes a stream"
|
||||
@spec get_topic(stream :: String.t(), User.t() | nil, Token.t() | nil, Map.t()) ::
|
||||
{:ok, topic :: String.t()} | {:error, :bad_topic}
|
||||
def get_topic(stream, user, oauth_token, params \\ %{})
|
||||
|
||||
# Allow all public steams.
|
||||
def get_topic(stream, _user, _oauth_token, _params) when stream in @public_streams do
|
||||
{:ok, stream}
|
||||
# Allow all public steams if the instance allows unauthenticated access.
|
||||
# Otherwise, only allow users with valid oauth tokens.
|
||||
def get_topic(stream, user, oauth_token, _params) when stream in @public_streams do
|
||||
kind = if stream in @local_streams, do: :local, else: :federated
|
||||
|
||||
if can_access_stream(user, oauth_token, kind) do
|
||||
{:ok, stream}
|
||||
else
|
||||
{:error, :unauthorized}
|
||||
end
|
||||
end
|
||||
|
||||
# Allow all hashtags streams.
|
||||
|
|
@ -57,12 +81,20 @@ defmodule Pleroma.Web.Streamer do
|
|||
end
|
||||
|
||||
# Allow remote instance streams.
|
||||
def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||
{:ok, "public:remote:" <> instance}
|
||||
def get_topic("public:remote", user, oauth_token, %{"instance" => instance} = _params) do
|
||||
if can_access_stream(user, oauth_token, :federated) do
|
||||
{:ok, "public:remote:" <> instance}
|
||||
else
|
||||
{:error, :unauthorized}
|
||||
end
|
||||
end
|
||||
|
||||
def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||
{:ok, "public:remote:media:" <> instance}
|
||||
def get_topic("public:remote:media", user, oauth_token, %{"instance" => instance} = _params) do
|
||||
if can_access_stream(user, oauth_token, :federated) do
|
||||
{:ok, "public:remote:media:" <> instance}
|
||||
else
|
||||
{:error, :unauthorized}
|
||||
end
|
||||
end
|
||||
|
||||
# Expand user streams.
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.TransmogrifierWorker do
|
||||
alias Pleroma.User
|
||||
|
||||
use Pleroma.Workers.WorkerHelper, queue: "transmogrifier"
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Job{args: %{"op" => "user_upgrade", "user_id" => user_id}}) do
|
||||
user = User.get_cached_by_id(user_id)
|
||||
Pleroma.Web.ActivityPub.Transmogrifier.perform(:user_upgrade, user)
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def timeout(_job), do: :timer.seconds(5)
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue