Merge remote-tracking branch 'origin/develop' into HEAD

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-02-22 11:59:15 +01:00
commit c592a0e58d
184 changed files with 1738 additions and 1213 deletions

6
.dialyzer_ignore.exs Normal file
View File

@ -0,0 +1,6 @@
[
{"lib/cachex.ex", "Unknown type: Spec.cache/0."},
{"lib/pleroma/web/plugs/rate_limiter.ex", "The pattern can never match the type {:commit, _} | {:ignore, _}."},
{"lib/pleroma/web/plugs/rate_limiter.ex", "Function get_scale/2 will never be called."},
{"lib/pleroma/web/plugs/rate_limiter.ex", "Function initialize_buckets!/1 will never be called."}
]

5
.gitignore vendored
View File

@ -57,5 +57,6 @@ pleroma.iml
.tool-versions .tool-versions
# Editor temp files # Editor temp files
/*~ *~
/*# *#
*.swp

View File

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.6.2
### Security
- MRF StealEmojiPolicy: Sanitize shortcodes (thanks to Hazel K for the report
## 2.6.1 ## 2.6.1
### Changed ### Changed
- - Document maximum supported version of Erlang & Elixir - - Document maximum supported version of Erlang & Elixir

View File

View File

@ -0,0 +1 @@
Support Bandit as an alternative to Cowboy for the HTTP server.

View File

@ -0,0 +1 @@
Fix federation with Convergence AP Bridge

View File

@ -0,0 +1 @@
- Config: Check the permissions of the linked file instead of the symlink

View File

@ -0,0 +1 @@
MediaProxy was setting the content-length header which is not permitted by RFC9112§6.2 when we are chunking the reply as it conflicts with the existence of the transfer-encoding header.

View File

View File

View File

View File

View File

View File

0
changelog.d/exile.skip Normal file
View File

View File

1
changelog.d/gun_pool.fix Normal file
View File

@ -0,0 +1 @@
Fix logic error in Gun connection pooling which prevented retries even when the worker was launched with retry = true

View File

@ -0,0 +1 @@
Mastodon API /api/v1/directory: Fix listing directory contents when not authenticated

1
changelog.d/memleak.fix Normal file
View File

@ -0,0 +1 @@
Fix a memory leak caused by Websocket connections that would not enter a state where a full garbage collection run could be triggered.

View File

View File

@ -0,0 +1 @@
Federated timeline removal of hashtags via MRF HashtagPolicy

View File

@ -0,0 +1 @@
Fix notifications query which was not using the index properly

View File

@ -0,0 +1 @@
Use User.full_nickname/1 in oauth html template

View File

@ -0,0 +1 @@
Rich Media Preview cache eviction when the activity is updated.

View File

1
changelog.d/tesla.deps Normal file
View File

@ -0,0 +1 @@
Update Tesla HTTP client middleware to 1.8.0

View File

@ -0,0 +1 @@
Refactor the Mastodon /api/v1/streaming websocket handler to use Phoenix.Socket.Transport

View File

@ -1,4 +1,4 @@
FROM elixir:1.12.3 FROM elixir:1.15.7-otp-25
# Single RUN statement, otherwise intermediate images are created # Single RUN statement, otherwise intermediate images are created
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run

View File

@ -114,14 +114,7 @@ config :pleroma, :uri_schemes,
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],
http: [ http: [
ip: {127, 0, 0, 1}, ip: {127, 0, 0, 1}
dispatch: [
{:_,
[
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
{:_, Plug.Cowboy.Handler, {Pleroma.Web.Endpoint, []}}
]}
]
], ],
protocol: "https", protocol: "https",
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
@ -911,6 +904,8 @@ config :pleroma, Pleroma.Application,
max_restarts: 3, max_restarts: 3,
streamer_registry: true streamer_registry: true
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 30_000
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View File

@ -8,8 +8,7 @@ import Config
# with brunch.io to recompile .js and .css sources. # with brunch.io to recompile .js and .css sources.
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
http: [ http: [
port: 4000, port: 4000
protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192]
], ],
protocol: "http", protocol: "http",
debug_errors: true, debug_errors: true,

View File

@ -170,6 +170,10 @@ config :pleroma, Pleroma.Application,
streamer_registry: false, streamer_registry: false,
test_http_pools: true test_http_pools: true
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 1_000
config :pleroma, Pleroma.Emoji.Loader, test_emoji: true
if File.exists?("./config/test.secret.exs") do if File.exists?("./config/test.secret.exs") do
import_config "test.secret.exs" import_config "test.secret.exs"
else else

View File

@ -111,7 +111,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
{:ok, _} = {:ok, _} =
:zip.unzip(binary_archive, :zip.unzip(binary_archive,
cwd: pack_path, cwd: String.to_charlist(pack_path),
file_list: files_to_unzip file_list: files_to_unzip
) )

View File

@ -1,93 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Phoenix.Transports.WebSocket.Raw do
import Plug.Conn,
only: [
fetch_query_params: 1,
send_resp: 3
]
alias Phoenix.Socket.Transport
def default_config do
[
timeout: 60_000,
transport_log: false,
cowboy: Phoenix.Endpoint.CowboyWebSocket
]
end
def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
{_, opts} = handler.__transport__(transport)
conn =
conn
|> fetch_query_params
|> Transport.transport_log(opts[:transport_log])
|> Transport.check_origin(handler, endpoint, opts)
case conn do
%{halted: false} = conn ->
case handler.connect(%{
endpoint: endpoint,
transport: transport,
options: [serializer: nil],
params: conn.params
}) do
{:ok, socket} ->
{:ok, conn, {__MODULE__, {socket, opts}}}
:error ->
send_resp(conn, :forbidden, "")
{:error, conn}
end
_ ->
{:error, conn}
end
end
def init(conn, _) do
send_resp(conn, :bad_request, "")
{:error, conn}
end
def ws_init({socket, config}) do
Process.flag(:trap_exit, true)
{:ok, %{socket: socket}, config[:timeout]}
end
def ws_handle(op, data, state) do
state.socket.handler
|> apply(:handle, [op, data, state])
|> case do
{op, data} ->
{:reply, {op, data}, state}
{op, data, state} ->
{:reply, {op, data}, state}
%{} = state ->
{:ok, state}
_ ->
{:ok, state}
end
end
def ws_info({_, _} = tuple, state) do
{:reply, tuple, state}
end
def ws_info(_tuple, state), do: {:ok, state}
def ws_close(state) do
ws_handle(:closed, :normal, state)
end
def ws_terminate(reason, state) do
ws_handle(:closed, reason, state)
end
end

View File

@ -28,7 +28,7 @@ defmodule Pleroma.Activity.HTML do
end end
end end
defp add_cache_key_for(activity_id, additional_key) do def add_cache_key_for(activity_id, additional_key) do
current = get_cache_keys_for(activity_id) current = get_cache_keys_for(activity_id)
unless additional_key in current do unless additional_key in current do

View File

@ -23,19 +23,21 @@ defmodule Pleroma.Announcement do
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)
end end
def change(struct, params \\ %{}) do @doc "Generates changeset for %Pleroma.Announcement{}"
struct @spec changeset(%__MODULE__{}, map()) :: %Ecto.Changeset{}
|> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered]) def changeset(announcement \\ %__MODULE__{}, params \\ %{data: %{}}) do
announcement
|> cast(validate_params(announcement, params), [:data, :starts_at, :ends_at, :rendered])
|> validate_required([:data]) |> validate_required([:data])
end end
defp validate_params(struct, params) do defp validate_params(announcement, params) do
base_data = base_data =
%{ %{
"content" => "", "content" => "",
"all_day" => false "all_day" => false
} }
|> Map.merge((struct && struct.data) || %{}) |> Map.merge((announcement && announcement.data) || %{})
merged_data = merged_data =
Map.merge(base_data, params.data) Map.merge(base_data, params.data)
@ -61,13 +63,13 @@ defmodule Pleroma.Announcement do
end end
def add(params) do def add(params) do
changeset = change(%__MODULE__{}, params) changeset = changeset(%__MODULE__{}, params)
Repo.insert(changeset) Repo.insert(changeset)
end end
def update(announcement, params) do def update(announcement, params) do
changeset = change(announcement, params) changeset = changeset(announcement, params)
Repo.update(changeset) Repo.update(changeset)
end end

View File

@ -8,10 +8,13 @@ defmodule Pleroma.Caching do
@callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()} @callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
@callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()} @callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()}
@callback fetch!(Cachex.cache(), any(), function() | nil) :: any() @callback fetch!(Cachex.cache(), any(), function() | nil) :: any()
@callback fetch(Cachex.cache(), any(), function() | nil) ::
{atom(), any()} | {atom(), any(), any()}
# @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()} # @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
@callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()} @callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
@callback stream!(Cachex.cache(), any()) :: Enumerable.t() @callback stream!(Cachex.cache(), any()) :: Enumerable.t()
@callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()} @callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
@callback expire(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
@callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()} @callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
@callback execute!(Cachex.cache(), function()) :: any() @callback execute!(Cachex.cache(), function()) :: any()
@callback get_and_update(Cachex.cache(), any(), function()) :: @callback get_and_update(Cachex.cache(), any(), function()) ::

View File

@ -256,7 +256,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
move_namespace_and_warn(@mrf_config_map, warning_preface) move_namespace_and_warn(@mrf_config_map, warning_preface)
end end
@spec move_namespace_and_warn([config_map()], String.t()) :: :ok | nil @spec move_namespace_and_warn([config_map()], String.t()) :: :ok | :error
def move_namespace_and_warn(config_map, warning_preface) do def move_namespace_and_warn(config_map, warning_preface) do
warning = warning =
Enum.reduce(config_map, "", fn Enum.reduce(config_map, "", fn
@ -279,7 +279,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
end end
end end
@spec check_media_proxy_whitelist_config() :: :ok | nil @spec check_media_proxy_whitelist_config() :: :ok | :error
def check_media_proxy_whitelist_config do def check_media_proxy_whitelist_config do
whitelist = Config.get([:media_proxy, :whitelist]) whitelist = Config.get([:media_proxy, :whitelist])
@ -340,7 +340,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
end end
end end
@spec check_activity_expiration_config() :: :ok | nil @spec check_activity_expiration_config() :: :ok | :error
def check_activity_expiration_config do def check_activity_expiration_config do
warning_preface = """ warning_preface = """
!!!DEPRECATION WARNING!!! !!!DEPRECATION WARNING!!!
@ -356,7 +356,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
) )
end end
@spec check_remote_ip_plug_name() :: :ok | nil @spec check_remote_ip_plug_name() :: :ok | :error
def check_remote_ip_plug_name do def check_remote_ip_plug_name do
warning_preface = """ warning_preface = """
!!!DEPRECATION WARNING!!! !!!DEPRECATION WARNING!!!
@ -372,7 +372,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
) )
end end
@spec check_uploaders_s3_public_endpoint() :: :ok | nil @spec check_uploaders_s3_public_endpoint() :: :ok | :error
def check_uploaders_s3_public_endpoint do def check_uploaders_s3_public_endpoint do
s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3]) s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3])
@ -393,7 +393,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
end end
end end
@spec check_old_chat_shoutbox() :: :ok | nil @spec check_old_chat_shoutbox() :: :ok | :error
def check_old_chat_shoutbox do def check_old_chat_shoutbox do
instance_config = Pleroma.Config.get([:instance]) instance_config = Pleroma.Config.get([:instance])
chat_config = Pleroma.Config.get([:chat]) || [] chat_config = Pleroma.Config.get([:chat]) || []

View File

@ -21,7 +21,7 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do
with_runtime_config = with_runtime_config =
if File.exists?(config_path) do if File.exists?(config_path) do
# <https://git.pleroma.social/pleroma/pleroma/-/issues/3135> # <https://git.pleroma.social/pleroma/pleroma/-/issues/3135>
%File.Stat{mode: mode} = File.lstat!(config_path) %File.Stat{mode: mode} = File.stat!(config_path)
if Bitwise.band(mode, 0o007) > 0 do if Bitwise.band(mode, 0o007) > 0 do
raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}" raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}"

View File

@ -57,7 +57,7 @@ defmodule Pleroma.Conversation do
3. Bump all relevant participations to 'unread' 3. Bump all relevant participations to 'unread'
""" """
def create_or_bump_for(activity, opts \\ []) do def create_or_bump_for(activity, opts \\ []) do
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity), with true <- Pleroma.Web.ActivityPub.Visibility.direct?(activity),
"Create" <- activity.data["type"], "Create" <- activity.data["type"],
%Object{} = object <- Object.normalize(activity, fetch: false), %Object{} = object <- Object.normalize(activity, fetch: false),
true <- object.data["type"] in ["Note", "Question"], true <- object.data["type"] in ["Note", "Question"],

View File

@ -12,6 +12,8 @@ defmodule Pleroma.DataMigration do
import Ecto.Changeset import Ecto.Changeset
import Ecto.Query import Ecto.Query
@type t :: %__MODULE__{}
schema "data_migrations" do schema "data_migrations" do
field(:name, :string) field(:name, :string)
field(:state, State, default: :pending) field(:state, State, default: :pending)

View File

@ -51,12 +51,12 @@ defmodule Pleroma.Emoji do
end end
@doc "Returns the path of the emoji `name`." @doc "Returns the path of the emoji `name`."
@spec get(String.t()) :: String.t() | nil @spec get(String.t()) :: Pleroma.Emoji.t() | nil
def get(name) do def get(name) do
name = maybe_strip_name(name) name = maybe_strip_name(name)
case :ets.lookup(@ets, name) do case :ets.lookup(@ets, name) do
[{_, path}] -> path [{_, emoji}] -> emoji
_ -> nil _ -> nil
end end
end end
@ -138,23 +138,23 @@ defmodule Pleroma.Emoji do
emojis = emojis ++ regional_indicators emojis = emojis ++ regional_indicators
for emoji <- emojis do for emoji <- emojis do
def is_unicode_emoji?(unquote(emoji)), do: true def unicode?(unquote(emoji)), do: true
end end
def is_unicode_emoji?(_), do: false def unicode?(_), do: false
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/ @emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s) def custom?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
def is_custom_emoji?(_), do: false def custom?(_), do: false
def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":") def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":")
def maybe_strip_name(name), do: name def maybe_strip_name(name), do: name
def maybe_quote(name) when is_binary(name) do def maybe_quote(name) when is_binary(name) do
if is_unicode_emoji?(name) do if unicode?(name) do
name name
else else
if String.starts_with?(name, ":") do if String.starts_with?(name, ":") do

View File

@ -15,8 +15,6 @@ defmodule Pleroma.Emoji.Loader do
require Logger require Logger
@mix_env Mix.env()
@type pattern :: Regex.t() | module() | String.t() @type pattern :: Regex.t() | module() | String.t()
@type patterns :: pattern() | [pattern()] @type patterns :: pattern() | [pattern()]
@type group_patterns :: keyword(patterns()) @type group_patterns :: keyword(patterns())
@ -79,7 +77,7 @@ defmodule Pleroma.Emoji.Loader do
# for testing emoji.txt entries we do not want exposed in normal operation # for testing emoji.txt entries we do not want exposed in normal operation
test_emoji = test_emoji =
if @mix_env == :test do if Application.get_env(:pleroma, __MODULE__)[:test_emoji] do
load_from_file("test/config/emoji.txt", emoji_groups) load_from_file("test/config/emoji.txt", emoji_groups)
else else
[] []

View File

@ -100,7 +100,7 @@ defmodule Pleroma.Emoji.Pack do
{:ok, _emoji_files} = {:ok, _emoji_files} =
:zip.unzip( :zip.unzip(
to_charlist(file.path), to_charlist(file.path),
[{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, tmp_dir}] [{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, String.to_charlist(tmp_dir)}]
) )
{_, updated_pack} = {_, updated_pack} =

View File

@ -216,9 +216,6 @@ defmodule Pleroma.Filter do
:re -> :re ->
~r/\b#{phrases}\b/i ~r/\b#{phrases}\b/i
_ ->
nil
end end
end end

View File

@ -114,7 +114,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
def response("/notices/" <> id) do def response("/notices/" <> id) do
with %Activity{} = activity <- Activity.get_by_id(id), with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.is_public?(activity) do true <- Visibility.public?(activity) do
activities = activities =
ActivityPub.fetch_activities_for_context(activity.data["context"]) ActivityPub.fetch_activities_for_context(activity.data["context"])
|> render_activities |> render_activities

View File

@ -9,7 +9,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
def start_monitor do def start_monitor do
pid = pid =
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do case GenServer.start_link(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
{:ok, pid} -> {:ok, pid} ->
pid pid

View File

@ -21,7 +21,9 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
def start_worker(opts, retry \\ false) do def start_worker(opts, retry \\ false) do
case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do
{:error, :max_children} -> {:error, :max_children} ->
if retry or free_pool() == :error do funs = [fn -> !retry end, fn -> match?(:error, free_pool()) end]
if Enum.any?(funs, fn fun -> fun.() end) do
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts}) :telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
{:error, :pool_full} {:error, :pool_full}
else else

View File

@ -43,89 +43,28 @@ defmodule Pleroma.Helpers.MediaHelper do
def video_framegrab(url) do def video_framegrab(url) do
with executable when is_binary(executable) <- System.find_executable("ffmpeg"), with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
{:ok, env} <- HTTP.get(url, [], pool: :media), {:ok, env} <- HTTP.get(url, [], pool: :media),
{:ok, fifo_path} <- mkfifo(), {:ok, pid} <- StringIO.open(env.body) do
args = [ body_stream = IO.binstream(pid, 1)
"-y",
"-i", Exile.stream!(
fifo_path, [
"-vframes", executable,
"1", "-i",
"-f", "pipe:0",
"mjpeg", "-vframes",
"-loglevel", "1",
"error", "-f",
"-" "mjpeg",
] do "pipe:1"
run_fifo(fifo_path, env, executable, args) ],
input: body_stream,
ignore_epipe: true,
stderr: :disable
)
|> Enum.into(<<>>)
else else
nil -> {:error, {:ffmpeg, :command_not_found}} nil -> {:error, {:ffmpeg, :command_not_found}}
{:error, _} = error -> error {:error, _} = error -> error
end end
end end
defp run_fifo(fifo_path, env, executable, args) do
pid =
Port.open({:spawn_executable, executable}, [
:use_stdio,
:stream,
:exit_status,
:binary,
args: args
])
fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out])
fix = Pleroma.Helpers.QtFastStart.fix(env.body)
true = Port.command(fifo, fix)
:erlang.port_close(fifo)
loop_recv(pid)
after
File.rm(fifo_path)
end
defp mkfifo do
path = Path.join(System.tmp_dir!(), "pleroma-media-preview-pipe-#{Ecto.UUID.generate()}")
case System.cmd("mkfifo", [path]) do
{_, 0} ->
spawn(fifo_guard(path))
{:ok, path}
{_, err} ->
{:error, {:fifo_failed, err}}
end
end
defp fifo_guard(path) do
pid = self()
fn ->
ref = Process.monitor(pid)
receive do
{:DOWN, ^ref, :process, ^pid, _} ->
File.rm(path)
end
end
end
defp loop_recv(pid) do
loop_recv(pid, <<>>)
end
defp loop_recv(pid, acc) do
receive do
{^pid, {:data, data}} ->
loop_recv(pid, acc <> data)
{^pid, {:exit_status, 0}} ->
{:ok, acc}
{^pid, {:exit_status, status}} ->
{:error, status}
after
5000 ->
:erlang.port_close(pid)
{:error, :timeout}
end
end
end end

View File

@ -126,9 +126,15 @@ defmodule Pleroma.Helpers.QtFastStart do
<<pos::integer-big-size(unquote(size)), rest::bits>>, <<pos::integer-big-size(unquote(size)), rest::bits>>,
acc acc
) do ) do
rewrite_entries(unquote(size), offset, rest, [ rewrite_entries(
acc | <<pos + offset::integer-big-size(unquote(size))>> unquote(size),
]) offset,
rest,
acc ++
[
<<pos + offset::integer-big-size(unquote(size))>>
]
)
end end
end end

View File

@ -6,8 +6,6 @@ defmodule Pleroma.HTML do
# Scrubbers are compiled on boot so they can be configured in OTP releases # Scrubbers are compiled on boot so they can be configured in OTP releases
# @on_load :compile_scrubbers # @on_load :compile_scrubbers
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
def compile_scrubbers do def compile_scrubbers do
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers") dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
@ -67,27 +65,20 @@ defmodule Pleroma.HTML do
end end
end end
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object) @spec extract_first_external_url_from_object(Pleroma.Object.t()) ::
{:ok, String.t()} | {:error, :no_content}
def extract_first_external_url_from_object(%{data: %{"content" => content}})
when is_binary(content) do when is_binary(content) do
unless object.data["fake"] do url =
key = "URL|#{object.id}" content
|> Floki.parse_fragment!()
|> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
|> Enum.take(1)
|> Floki.attribute("href")
|> Enum.at(0)
@cachex.fetch!(:scrubber_cache, key, fn _key -> {:ok, url}
{:commit, {:ok, extract_first_external_url(content)}}
end)
else
{:ok, extract_first_external_url(content)}
end
end end
def extract_first_external_url_from_object(_), do: {:error, :no_content} def extract_first_external_url_from_object(_), do: {:error, :no_content}
def extract_first_external_url(content) do
content
|> Floki.parse_fragment!()
|> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
|> Enum.take(1)
|> Floki.attribute("href")
|> Enum.at(0)
end
end end

View File

@ -15,8 +15,8 @@ defmodule Pleroma.HTTP.AdapterHelper do
require Logger require Logger
@type proxy :: @type proxy ::
{Connection.host(), pos_integer()} {host(), pos_integer()}
| {Connection.proxy_type(), Connection.host(), pos_integer()} | {proxy_type(), host(), pos_integer()}
@callback options(keyword(), URI.t()) :: keyword() @callback options(keyword(), URI.t()) :: keyword()

View File

@ -54,12 +54,12 @@ defmodule Pleroma.HTTP.RequestBuilder do
@doc """ @doc """
Add optional parameters to the request Add optional parameters to the request
""" """
@spec add_param(Request.t(), atom(), atom(), any()) :: Request.t() @spec add_param(Request.t(), atom(), atom() | String.t(), any()) :: Request.t()
def add_param(request, :query, :query, values), do: %{request | query: values} def add_param(request, :query, :query, values), do: %{request | query: values}
def add_param(request, :body, :body, value), do: %{request | body: value} def add_param(request, :body, :body, value), do: %{request | body: value}
def add_param(request, :body, key, value) do def add_param(request, :body, key, value) when is_binary(key) do
request request
|> Map.put(:body, Multipart.new()) |> Map.put(:body, Multipart.new())
|> Map.update!( |> Map.update!(

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Maps do defmodule Pleroma.Maps do
@ -18,4 +18,17 @@ defmodule Pleroma.Maps do
rescue rescue
_ -> data _ -> data
end end
def filter_empty_values(data) do
# TODO: Change to Map.filter in Elixir 1.13+
data
|> Enum.filter(fn
{_k, nil} -> false
{_k, ""} -> false
{_k, []} -> false
{_k, %{} = v} -> Map.keys(v) != []
{_k, _v} -> true
end)
|> Map.new()
end
end end

View File

@ -77,7 +77,7 @@ defmodule Pleroma.MFA do
{:ok, codes} {:ok, codes}
else else
{:error, msg} -> {:error, msg} ->
%{error: msg} {:error, msg}
end end
end end

View File

@ -14,6 +14,7 @@ defmodule Pleroma.MFA.TOTP do
@doc """ @doc """
https://github.com/google/google-authenticator/wiki/Key-Uri-Format https://github.com/google/google-authenticator/wiki/Key-Uri-Format
""" """
@spec provisioning_uri(String.t(), String.t(), list()) :: String.t()
def provisioning_uri(secret, label, opts \\ []) do def provisioning_uri(secret, label, opts \\ []) do
query = query =
%{ %{
@ -27,7 +28,7 @@ defmodule Pleroma.MFA.TOTP do
|> URI.encode_query() |> URI.encode_query()
%URI{scheme: "otpauth", host: "totp", path: "/" <> label, query: query} %URI{scheme: "otpauth", host: "totp", path: "/" <> label, query: query}
|> URI.to_string() |> to_string()
end end
defp default_period, do: Config.get(@config_ns ++ [:period]) defp default_period, do: Config.get(@config_ns ++ [:period])

View File

@ -188,10 +188,11 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do
end end
defp fault_rate do defp fault_rate do
with failures_count when is_integer(failures_count) <- failures_count() do with failures_count when is_integer(failures_count) <- failures_count(),
true <- failures_count > 0 do
failures_count / Enum.max([get_stat(:affected_count, 0), 1]) failures_count / Enum.max([get_stat(:affected_count, 0), 1])
else else
_ -> :error _ -> 0
end end
end end

View File

@ -121,7 +121,7 @@ defmodule Pleroma.ModerationLog do
defp prepare_log_data(attrs), do: attrs defp prepare_log_data(attrs), do: attrs
@spec insert_log(log_params()) :: {:ok, ModerationLog} | {:error, any} @spec insert_log(log_params()) :: {:ok, ModerationLog.t()} | {:error, any}
def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do
data = data =
attrs attrs
@ -248,7 +248,8 @@ defmodule Pleroma.ModerationLog do
|> insert_log_entry_with_message() |> insert_log_entry_with_message()
end end
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any} @spec insert_log_entry_with_message(ModerationLog.t()) ::
{:ok, ModerationLog.t()} | {:error, any}
defp insert_log_entry_with_message(entry) do defp insert_log_entry_with_message(entry) do
entry.data["message"] entry.data["message"]
|> put_in(get_log_entry_message(entry)) |> put_in(get_log_entry_message(entry))

View File

@ -88,7 +88,7 @@ defmodule Pleroma.Notification do
where: q.seen == true, where: q.seen == true,
select: type(q.id, :string), select: type(q.id, :string),
limit: 1, limit: 1,
order_by: [desc: :id] order_by: fragment("? desc nulls last", q.id)
) )
end end

View File

@ -242,17 +242,17 @@ defmodule Pleroma.Object do
{:ok, _} <- invalid_object_cache(object) do {:ok, _} <- invalid_object_cache(object) do
cleanup_attachments( cleanup_attachments(
Config.get([:instance, :cleanup_attachments]), Config.get([:instance, :cleanup_attachments]),
%{"object" => object} object
) )
{:ok, object, deleted_activity} {:ok, object, deleted_activity}
end end
end end
@spec cleanup_attachments(boolean(), %{required(:object) => map()}) :: @spec cleanup_attachments(boolean(), Object.t()) ::
{:ok, Oban.Job.t() | nil} {:ok, Oban.Job.t() | nil}
def cleanup_attachments(true, %{"object" => _} = params) do def cleanup_attachments(true, %Object{} = object) do
AttachmentsCleanupWorker.enqueue("cleanup_attachments", params) AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{"object" => object})
end end
def cleanup_attachments(_, _), do: {:ok, nil} def cleanup_attachments(_, _), do: {:ok, nil}

View File

@ -61,15 +61,16 @@ defmodule Pleroma.Pagination do
|> Repo.all() |> Repo.all()
end end
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()] @spec paginate_list(list(), keyword()) :: list()
def paginate(query, options, method \\ :keyset, table_binding \\ nil) def paginate_list(list, options) do
def paginate(list, options, _method, _table_binding) when is_list(list) do
offset = options[:offset] || 0 offset = options[:offset] || 0
limit = options[:limit] || 0 limit = options[:limit] || 0
Enum.slice(list, offset, limit) Enum.slice(list, offset, limit)
end end
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
def paginate(query, options, :keyset, table_binding) do def paginate(query, options, :keyset, table_binding) do
query query
|> restrict(:min_id, options, table_binding) |> restrict(:min_id, options, table_binding)

View File

@ -28,7 +28,7 @@ defmodule Pleroma.Password.Pbkdf2 do
iterations = String.to_integer(iterations) iterations = String.to_integer(iterations)
digest = String.to_atom(digest) digest = String.to_existing_atom(digest)
binary_hash = binary_hash =
KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64) KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64)

View File

@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do
~w(if-unmodified-since if-none-match) ++ @range_headers ~w(if-unmodified-since if-none-match) ++ @range_headers
@resp_cache_headers ~w(etag date last-modified) @resp_cache_headers ~w(etag date last-modified)
@keep_resp_headers @resp_cache_headers ++ @keep_resp_headers @resp_cache_headers ++
~w(content-length content-type content-disposition content-encoding) ++ ~w(content-type content-disposition content-encoding) ++
~w(content-range accept-ranges vary) ~w(content-range accept-ranges vary)
@default_cache_control_header "public, max-age=1209600" @default_cache_control_header "public, max-age=1209600"
@valid_resp_codes [200, 206, 304] @valid_resp_codes [200, 206, 304]
@ -84,13 +84,13 @@ defmodule Pleroma.ReverseProxy do
{:max_read_duration, non_neg_integer() | :infinity} {:max_read_duration, non_neg_integer() | :infinity}
| {:max_body_length, non_neg_integer() | :infinity} | {:max_body_length, non_neg_integer() | :infinity}
| {:failed_request_ttl, non_neg_integer() | :infinity} | {:failed_request_ttl, non_neg_integer() | :infinity}
| {:http, []} | {:http, keyword()}
| {:req_headers, [{String.t(), String.t()}]} | {:req_headers, [{String.t(), String.t()}]}
| {:resp_headers, [{String.t(), String.t()}]} | {:resp_headers, [{String.t(), String.t()}]}
| {:inline_content_types, boolean() | [String.t()]} | {:inline_content_types, boolean() | list(String.t())}
| {:redirect_on_failure, boolean()} | {:redirect_on_failure, boolean()}
@spec call(Plug.Conn.t(), url :: String.t(), [option()]) :: Plug.Conn.t() @spec call(Plug.Conn.t(), String.t(), list(option())) :: Plug.Conn.t()
def call(_conn, _url, _opts \\ []) def call(_conn, _url, _opts \\ [])
def call(conn = %{method: method}, url, opts) when method in @methods do def call(conn = %{method: method}, url, opts) when method in @methods do
@ -388,8 +388,6 @@ defmodule Pleroma.ReverseProxy do
defp body_size_constraint(_, _), do: :ok defp body_size_constraint(_, _), do: :ok
defp check_read_duration(nil = _duration, max), do: check_read_duration(@max_read_duration, max)
defp check_read_duration(duration, max) defp check_read_duration(duration, max)
when is_integer(duration) and is_integer(max) and max > 0 do when is_integer(duration) and is_integer(max) and max > 0 do
if duration > max do if duration > max do
@ -407,10 +405,6 @@ defmodule Pleroma.ReverseProxy do
{:ok, previous_duration + duration} {:ok, previous_duration + duration}
end end
defp increase_read_duration(_) do
{:ok, :no_duration_limit, :no_duration_limit}
end
defp client, do: Pleroma.ReverseProxy.Client.Wrapper defp client, do: Pleroma.ReverseProxy.Client.Wrapper
defp track_failed_url(url, error, opts) do defp track_failed_url(url, error, opts) do

View File

@ -20,5 +20,5 @@ defmodule Pleroma.Search.SearchBackend do
is what contains the actual content and there is no need for filtering when removing is what contains the actual content and there is no need for filtering when removing
from index. from index.
""" """
@callback remove_from_index(object :: Pleroma.Object.t()) :: {:ok, any()} | {:error, any()} @callback remove_from_index(object :: Pleroma.Object.t()) :: :ok | {:error, any()}
end end

View File

@ -27,7 +27,7 @@ defmodule Pleroma.Signature do
_ -> _ ->
case Pleroma.Web.WebFinger.finger(maybe_ap_id) do case Pleroma.Web.WebFinger.finger(maybe_ap_id) do
%{"ap_id" => ap_id} -> {:ok, ap_id} {:ok, %{"ap_id" => ap_id}} -> {:ok, ap_id}
_ -> {:error, maybe_ap_id} _ -> {:error, maybe_ap_id}
end end
end end

View File

@ -59,7 +59,7 @@ defmodule Pleroma.Telemetry.Logger do
_, _,
_ _
) do ) do
Logger.error(fn -> Logger.debug(fn ->
"Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion" "Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion"
end) end)
end end
@ -81,7 +81,7 @@ defmodule Pleroma.Telemetry.Logger do
%{key: key, protocol: :http}, %{key: key, protocol: :http},
_ _
) do ) do
Logger.info(fn -> Logger.debug(fn ->
"Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur." "Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur."
end) end)
end end

View File

@ -51,6 +51,7 @@ defmodule Pleroma.Upload do
| {:size_limit, nil | non_neg_integer()} | {:size_limit, nil | non_neg_integer()}
| {:uploader, module()} | {:uploader, module()}
| {:filters, [module()]} | {:filters, [module()]}
| {:actor, String.t()}
@type t :: %__MODULE__{ @type t :: %__MODULE__{
id: String.t(), id: String.t(),
@ -175,7 +176,7 @@ defmodule Pleroma.Upload do
defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data) parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
data = Base.decode64!(parsed["data"], ignore: :whitespace) data = Base.decode64!(parsed["data"], ignore: :whitespace)
hash = Base.encode16(:crypto.hash(:sha256, data), lower: true) hash = Base.encode16(:crypto.hash(:sha256, data), case: :upper)
with :ok <- check_binary_size(data, opts.size_limit), with :ok <- check_binary_size(data, opts.size_limit),
tmp_path <- tempfile_for_image(data), tmp_path <- tempfile_for_image(data),

View File

@ -5,8 +5,6 @@
defmodule Pleroma.Uploaders.Uploader do defmodule Pleroma.Uploaders.Uploader do
import Pleroma.Web.Gettext import Pleroma.Web.Gettext
@mix_env Mix.env()
@moduledoc """ @moduledoc """
Defines the contract to put and get an uploaded file to any backend. Defines the contract to put and get an uploaded file to any backend.
""" """
@ -75,10 +73,5 @@ defmodule Pleroma.Uploaders.Uploader do
end end
end end
defp callback_timeout do defp callback_timeout, do: Application.get_env(:pleroma, __MODULE__)[:timeout]
case @mix_env do
:test -> 1_000
_ -> 30_000
end
end
end end

View File

@ -1787,7 +1787,10 @@ defmodule Pleroma.User do
@spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()} @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def set_activation(users, status) when is_list(users) do def set_activation(users, status) when is_list(users) do
Repo.transaction(fn -> Repo.transaction(fn ->
for user <- users, do: set_activation(user, status) for user <- users do
{:ok, user} = set_activation(user, status)
user
end
end) end)
end end
@ -2404,9 +2407,9 @@ defmodule Pleroma.User do
defp put_password_hash(changeset), do: changeset defp put_password_hash(changeset), do: changeset
def is_internal_user?(%User{nickname: nil}), do: true def internal?(%User{nickname: nil}), do: true
def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true def internal?(%User{local: true, nickname: "internal." <> _}), do: true
def is_internal_user?(_), do: false def internal?(_), do: false
# A hack because user delete activities have a fake id for whatever reason # A hack because user delete activities have a fake id for whatever reason
# TODO: Get rid of this # TODO: Get rid of this
@ -2556,9 +2559,9 @@ defmodule Pleroma.User do
cast(user, params, [:is_confirmed, :confirmation_token]) cast(user, params, [:is_confirmed, :confirmation_token])
end end
@spec approval_changeset(User.t(), keyword()) :: Ecto.Changeset.t() @spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
def approval_changeset(user, set_approval: approved?) do def approval_changeset(changeset, set_approval: approved?) do
cast(user, %{is_approved: approved?}, [:is_approved]) cast(changeset, %{is_approved: approved?}, [:is_approved])
end end
@spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()} @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}

View File

@ -22,6 +22,8 @@ defmodule Pleroma.User.Backup do
alias Pleroma.Web.ActivityPub.UserView alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Workers.BackupWorker alias Pleroma.Workers.BackupWorker
@type t :: %__MODULE__{}
schema "backups" do schema "backups" do
field(:content_type, :string) field(:content_type, :string)
field(:file_name, :string) field(:file_name, :string)
@ -195,6 +197,7 @@ defmodule Pleroma.User.Backup do
end end
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json'] @files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
@spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error
def export(%__MODULE__{} = backup, caller_pid) do def export(%__MODULE__{} = backup, caller_pid) do
backup = Repo.preload(backup, :user) backup = Repo.preload(backup, :user)
dir = backup_tempdir(backup) dir = backup_tempdir(backup)
@ -204,9 +207,11 @@ defmodule Pleroma.User.Backup do
:ok <- statuses(dir, backup.user, caller_pid), :ok <- statuses(dir, backup.user, caller_pid),
:ok <- likes(dir, backup.user, caller_pid), :ok <- likes(dir, backup.user, caller_pid),
:ok <- bookmarks(dir, backup.user, caller_pid), :ok <- bookmarks(dir, backup.user, caller_pid),
{:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir), {:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir),
{:ok, _} <- File.rm_rf(dir) do {:ok, _} <- File.rm_rf(dir) do
{:ok, to_string(zip_path)} {:ok, zip_path}
else
_ -> :error
end end
end end
@ -382,6 +387,8 @@ defmodule Pleroma.User.Backup.Processor do
[:file_size, :processed, :state] [:file_size, :processed, :state]
) )
|> Repo.update() |> Repo.update()
else
e -> {:error, e}
end end
end end
end end

View File

@ -71,7 +71,7 @@ defmodule Pleroma.User.Query do
@equal_criteria [:email] @equal_criteria [:email]
@contains_criteria [:ap_id, :nickname] @contains_criteria [:ap_id, :nickname]
@spec build(Query.t(), criteria()) :: Query.t() @spec build(Ecto.Query.t(), criteria()) :: Ecto.Query.t()
def build(query \\ base_query(), criteria) do def build(query \\ base_query(), criteria) do
prepare_query(query, criteria) prepare_query(query, criteria)
end end

View File

@ -14,6 +14,8 @@ defmodule Pleroma.UserRelationship do
alias Pleroma.User alias Pleroma.User
alias Pleroma.UserRelationship alias Pleroma.UserRelationship
@type t :: %__MODULE__{}
schema "user_relationships" do schema "user_relationships" do
belongs_to(:source, User, type: FlakeId.Ecto.CompatType) belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
belongs_to(:target, User, type: FlakeId.Ecto.CompatType) belongs_to(:target, User, type: FlakeId.Ecto.CompatType)

View File

@ -74,22 +74,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp check_remote_limit(_), do: true defp check_remote_limit(_), do: true
def increase_note_count_if_public(actor, object) do def increase_note_count_if_public(actor, object) do
if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor} if public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
end end
def decrease_note_count_if_public(actor, object) do def decrease_note_count_if_public(actor, object) do
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} if public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
end end
def update_last_status_at_if_public(actor, object) do def update_last_status_at_if_public(actor, object) do
if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} if public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
end end
defp increase_replies_count_if_reply(%{ defp increase_replies_count_if_reply(%{
"object" => %{"inReplyTo" => reply_ap_id} = object, "object" => %{"inReplyTo" => reply_ap_id} = object,
"type" => "Create" "type" => "Create"
}) do }) do
if is_public?(object) do if public?(object) do
Object.increase_replies_count(reply_ap_id) Object.increase_replies_count(reply_ap_id)
end end
end end
@ -100,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"object" => %{"quoteUrl" => quote_ap_id} = object, "object" => %{"quoteUrl" => quote_ap_id} = object,
"type" => "Create" "type" => "Create"
}) do }) do
if is_public?(object) do if public?(object) do
Object.increase_quotes_count(quote_ap_id) Object.increase_quotes_count(quote_ap_id)
end end
end end

View File

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
This module encodes our addressing policies and general shape of our objects. This module encodes our addressing policies and general shape of our objects.
""" """
alias Pleroma.Activity
alias Pleroma.Emoji alias Pleroma.Emoji
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
@ -131,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
def emoji_react(actor, object, emoji) do def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do with {:ok, data, meta} <- object_action(actor, object) do
data = data =
if Emoji.is_unicode_emoji?(emoji) do if Emoji.unicode?(emoji) do
unicode_emoji_react(object, data, emoji) unicode_emoji_react(object, data, emoji)
else else
custom_emoji_react(object, data, emoji) custom_emoji_react(object, data, emoji)
@ -347,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
actor.ap_id == Relay.ap_id() -> actor.ap_id == Relay.ap_id() ->
[actor.follower_address] [actor.follower_address]
public? and Visibility.is_local_public?(object) -> public? and Visibility.local_public?(object) ->
[actor.follower_address, object.data["actor"], Utils.as_local_public()] [actor.follower_address, object.data["actor"], Utils.as_local_public()]
public? -> public? ->
@ -375,7 +376,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
# Address the actor of the object, and our actor's follower collection if the post is public. # Address the actor of the object, and our actor's follower collection if the post is public.
to = to =
if Visibility.is_public?(object) do if Visibility.public?(object) do
[actor.follower_address, object.data["actor"]] [actor.follower_address, object.data["actor"]]
else else
[object.data["actor"]] [object.data["actor"]]

View File

@ -84,7 +84,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
if hashtags != [] do if hashtags != [] do
with {:ok, message} <- check_reject(message, hashtags), with {:ok, message} <- check_reject(message, hashtags),
{:ok, message} <- {:ok, message} <-
(if "type" == "Create" do (if type == "Create" do
check_ftl_removal(message, hashtags) check_ftl_removal(message, hashtags)
else else
{:ok, message} {:ok, message}

View File

@ -62,7 +62,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
key: :mrf_inline_quote, key: :mrf_inline_quote,
related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy", related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy",
label: "MRF Inline Quote Policy", label: "MRF Inline Quote Policy",
type: :group,
description: "Force quote url to appear in post content.", description: "Force quote url to appear in post content.",
children: [ children: [
%{ %{

View File

@ -10,15 +10,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
@moduledoc "Reject or Word-Replace messages with a keyword or regex" @moduledoc "Reject or Word-Replace messages with a keyword or regex"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp string_matches?(string, _) when not is_binary(string) do
false
end
defp string_matches?(string, pattern) when is_binary(pattern) do defp string_matches?(string, pattern) when is_binary(pattern) do
String.contains?(string, pattern) String.contains?(string, pattern)
end end
defp string_matches?(string, pattern) do defp string_matches?(string, %Regex{} = pattern) do
String.match?(string, pattern) String.match?(string, pattern)
end end

View File

@ -10,9 +10,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
@impl true @impl true
def filter(%{"actor" => actor} = object) do def filter(%{"actor" => actor} = object) do
with true <- is_local?(actor), with true <- local?(actor),
true <- is_eligible_type?(object), true <- eligible_type?(object),
true <- is_note?(object), true <- note?(object),
false <- has_attachment?(object), false <- has_attachment?(object),
true <- only_mentions?(object) do true <- only_mentions?(object) do
{:reject, "[NoEmptyPolicy]"} {:reject, "[NoEmptyPolicy]"}
@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
def filter(object), do: {:ok, object} def filter(object), do: {:ok, object}
defp is_local?(actor) do defp local?(actor) do
if actor |> String.starts_with?("#{Endpoint.url()}") do if actor |> String.starts_with?("#{Endpoint.url()}") do
true true
else else
@ -59,11 +59,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
defp only_mentions?(_), do: false defp only_mentions?(_), do: false
defp is_note?(%{"object" => %{"type" => "Note"}}), do: true defp note?(%{"object" => %{"type" => "Note"}}), do: true
defp is_note?(_), do: false defp note?(_), do: false
defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true defp eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
defp is_eligible_type?(_), do: false defp eligible_type?(_), do: false
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}

View File

@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do
tags = object["tag"] || [] tags = object["tag"] || []
if Enum.any?(tags, fn tag -> if Enum.any?(tags, fn tag ->
CommonFixes.is_object_link_tag(tag) and tag["href"] == quote_url CommonFixes.object_link_tag?(tag) and tag["href"] == quote_url
end) do end) do
object object
else else

View File

@ -36,6 +36,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
extension = if extension == "", do: ".png", else: extension extension = if extension == "", do: ".png", else: extension
shortcode = Path.basename(shortcode)
file_path = Path.join(emoji_dir_path, shortcode <> extension) file_path = Path.join(emoji_dir_path, shortcode <> extension)
case File.write(file_path, response.body) do case File.write(file_path, response.body) do
@ -78,6 +79,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
new_emojis = new_emojis =
foreign_emojis foreign_emojis
|> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end) |> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end)
|> Enum.reject(fn {shortcode, _url} -> String.contains?(shortcode, ["/", "\\"]) end)
|> Enum.filter(fn {shortcode, _url} -> |> Enum.filter(fn {shortcode, _url} ->
reject_emoji? = reject_emoji? =
[:mrf_steal_emoji, :rejected_shortcodes] [:mrf_steal_emoji, :rejected_shortcodes]

View File

@ -173,6 +173,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
{:object_validation, e} -> {:object_validation, e} ->
e e
{:error, %Ecto.Changeset{} = e} ->
{:error, e}
end end
end end

View File

@ -82,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
object when is_binary(object) <- get_field(cng, :object), object when is_binary(object) <- get_field(cng, :object),
%User{} = actor <- User.get_cached_by_ap_id(actor), %User{} = actor <- User.get_cached_by_ap_id(actor),
%Object{} = object <- Object.get_cached_by_ap_id(object), %Object{} = object <- Object.get_cached_by_ap_id(object),
false <- Visibility.is_public?(object) do false <- Visibility.public?(object) do
same_actor = object.data["actor"] == actor.ap_id same_actor = object.data["actor"] == actor.ap_id
recipients = get_field(cng, :to) ++ get_field(cng, :cc) recipients = get_field(cng, :to) ++ get_field(cng, :cc)
local_public = Utils.as_local_public() local_public = Utils.as_local_public()

View File

@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Maps
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Object.Containment alias Pleroma.Object.Containment
alias Pleroma.User alias Pleroma.User
@ -24,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
end end
def fix_object_defaults(data) do def fix_object_defaults(data) do
data = Maps.filter_empty_values(data)
context = context =
Utils.maybe_create_context( Utils.maybe_create_context(
data["context"] || data["conversation"] || data["inReplyTo"] || data["id"] data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
@ -99,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
end end
def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do
tag = Enum.find(tags, &is_object_link_tag/1) tag = Enum.find(tags, &object_link_tag?/1)
if not is_nil(tag) do if not is_nil(tag) do
data data
@ -112,7 +115,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
def fix_quote_url(data), do: data def fix_quote_url(data), do: data
# https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md # https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
def is_object_link_tag(%{ def object_link_tag?(%{
"type" => "Link", "type" => "Link",
"mediaType" => media_type, "mediaType" => media_type,
"href" => href "href" => href
@ -121,5 +124,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
true true
end end
def is_object_link_tag(_), do: false def object_link_tag?(_), do: false
end end

View File

@ -74,10 +74,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji) new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
cond do cond do
Pleroma.Emoji.is_unicode_emoji?(emoji) -> Pleroma.Emoji.unicode?(emoji) ->
data data
Pleroma.Emoji.is_unicode_emoji?(new_emoji) -> Pleroma.Emoji.unicode?(new_emoji) ->
data |> Map.put("content", new_emoji) data |> Map.put("content", new_emoji)
true -> true ->
@ -90,7 +90,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
defp validate_emoji(cng) do defp validate_emoji(cng) do
content = get_field(cng, :content) content = get_field(cng, :content)
if Emoji.is_unicode_emoji?(content) || Emoji.is_custom_emoji?(content) do if Emoji.unicode?(content) || Emoji.custom?(content) do
cng cng
else else
cng cng
@ -101,7 +101,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
defp maybe_validate_tag_presence(cng) do defp maybe_validate_tag_presence(cng) do
content = get_field(cng, :content) content = get_field(cng, :content)
if Emoji.is_unicode_emoji?(content) do if Emoji.unicode?(content) do
cng cng
else else
tag = get_field(cng, :tag) tag = get_field(cng, :tag)

View File

@ -62,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
with {:ok, local} <- Keyword.fetch(meta, :local) do with {:ok, local} <- Keyword.fetch(meta, :local) do
do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating]) do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating])
if !do_not_federate and local and not Visibility.is_local_public?(activity) do if !do_not_federate and local and not Visibility.local_public?(activity) do
activity = activity =
if object = Keyword.get(meta, :object_data) do if object = Keyword.get(meta, :object_data) do
%{activity | data: Map.put(activity.data, "object", object)} %{activity | data: Map.put(activity.data, "object", object)}

View File

@ -66,7 +66,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
@doc """ @doc """
Determine if an activity can be represented by running it through Transmogrifier. Determine if an activity can be represented by running it through Transmogrifier.
""" """
def is_representable?(%Activity{} = activity) do def representable?(%Activity{} = activity) do
with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do
true true
else else
@ -246,7 +246,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity) def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
when is_list(bcc) and bcc != [] do when is_list(bcc) and bcc != [] do
public = is_public?(activity) public = public?(activity)
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data) {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
[priority_recipients, recipients] = recipients(actor, activity) [priority_recipients, recipients] = recipients(actor, activity)
@ -291,7 +291,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
# Publishes an activity to all relevant peers. # Publishes an activity to all relevant peers.
def publish(%User{} = actor, %Activity{} = activity) do def publish(%User{} = actor, %Activity{} = activity) do
public = is_public?(activity) public = public?(activity)
if public && Config.get([:instance, :allow_relay]) do if public && Config.get([:instance, :allow_relay]) do
Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end) Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end)

View File

@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()} @spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
def publish(%Activity{data: %{"type" => "Create"}} = activity) do def publish(%Activity{data: %{"type" => "Create"}} = activity) do
with %User{} = user <- get_actor(), with %User{} = user <- get_actor(),
true <- Visibility.is_public?(activity) do true <- Visibility.public?(activity) do
CommonAPI.repeat(activity.id, user) CommonAPI.repeat(activity.id, user)
else else
error -> format_error(error) error -> format_error(error)

View File

@ -258,7 +258,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
Utils.add_announce_to_object(object, announced_object) Utils.add_announce_to_object(object, announced_object)
if !User.is_internal_user?(user) do if !User.internal?(user) do
Notification.create_notifications(object) Notification.create_notifications(object)
ap_streamer().stream_out(object) ap_streamer().stream_out(object)
@ -304,9 +304,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
result = result =
case deleted_object do case deleted_object do
%Object{} -> %Object{} ->
with {:ok, deleted_object, _activity} <- Object.delete(deleted_object), with {_, {:ok, deleted_object, _activity}} <- {:object, Object.delete(deleted_object)},
{_, actor} when is_binary(actor) <- {:actor, deleted_object.data["actor"]}, {_, actor} when is_binary(actor) <- {:actor, deleted_object.data["actor"]},
%User{} = user <- User.get_cached_by_ap_id(actor) do {_, %User{} = user} <- {:user, User.get_cached_by_ap_id(actor)} do
User.remove_pinned_object_id(user, deleted_object.data["id"]) User.remove_pinned_object_id(user, deleted_object.data["id"])
{:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object) {:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object)
@ -328,6 +328,17 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:actor, _} -> {:actor, _} ->
@logger.error("The object doesn't have an actor: #{inspect(deleted_object)}") @logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
:no_object_actor :no_object_actor
{:user, _} ->
@logger.error(
"The object's actor could not be resolved to a user: #{inspect(deleted_object)}"
)
:no_object_user
{:object, _} ->
@logger.error("The object could not be deleted: #{inspect(deleted_object)}")
{:error, object}
end end
%User{} -> %User{} ->
@ -569,7 +580,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
def handle_undoing(object), do: {:error, ["don't know how to handle", object]} def handle_undoing(object), do: {:error, ["don't know how to handle", object]}
@spec delete_object(Object.t()) :: :ok | {:error, Ecto.Changeset.t()} @spec delete_object(Activity.t()) :: :ok | {:error, Ecto.Changeset.t()}
defp delete_object(object) do defp delete_object(object) do
with {:ok, _} <- Repo.delete(object), do: :ok with {:ok, _} <- Repo.delete(object), do: :ok
end end

View File

@ -4,5 +4,5 @@
defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do
@callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} @callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
@callback handle_after_transaction(map()) :: map() @callback handle_after_transaction(keyword()) :: keyword()
end end

View File

@ -336,10 +336,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_tag(object), do: object def fix_tag(object), do: object
def fix_content_map(%{"contentMap" => nil} = object) do
Map.drop(object, ["contentMap"])
end
# content map usually only has one language so this will do for now. # content map usually only has one language so this will do for now.
def fix_content_map(%{"contentMap" => content_map} = object) do def fix_content_map(%{"contentMap" => content_map} = object) do
content_groups = Map.to_list(content_map) content_groups = Map.to_list(content_map)
@ -783,7 +779,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Object.normalize(fetch: false) |> Object.normalize(fetch: false)
data = data =
if Visibility.is_private?(object) && object.data["actor"] == ap_id do if Visibility.private?(object) && object.data["actor"] == ap_id do
data |> Map.put("object", object |> Map.get(:data) |> prepare_object) data |> Map.put("object", object |> Map.get(:data) |> prepare_object)
else else
data |> maybe_fix_object_url data |> maybe_fix_object_url

View File

@ -167,7 +167,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
with true <- Config.get!([:instance, :federating]), with true <- Config.get!([:instance, :federating]),
true <- type != "Block" || outgoing_blocks, true <- type != "Block" || outgoing_blocks,
false <- Visibility.is_local_public?(activity) do false <- Visibility.local_public?(activity) do
Pleroma.Web.Federator.publish(activity) Pleroma.Web.Federator.publish(activity)
end end
@ -277,7 +277,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
object_actor = User.get_cached_by_ap_id(object_actor_id) object_actor = User.get_cached_by_ap_id(object_actor_id)
to = to =
if Visibility.is_public?(object) do if Visibility.public?(object) do
[actor.follower_address, object.data["actor"]] [actor.follower_address, object.data["actor"]]
else else
[object.data["actor"]] [object.data["actor"]]
@ -776,10 +776,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do
build_flag_object(object) build_flag_object(object)
nil -> nil ->
if %Object{} = object = Object.get_by_ap_id(id) do case Object.get_by_ap_id(id) do
build_flag_object(object) %Object{} = object -> build_flag_object(object)
else _ -> %{"id" => id, "deleted" => true}
%{"id" => id, "deleted" => true}
end end
end end
end end

View File

@ -11,28 +11,28 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
require Pleroma.Constants require Pleroma.Constants
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean() @spec public?(Object.t() | Activity.t() | map()) :: boolean()
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false def public?(%Object{data: %{"type" => "Tombstone"}}), do: false
def is_public?(%Object{data: data}), do: is_public?(data) def public?(%Object{data: data}), do: public?(data)
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true def public?(%Activity{data: %{"type" => "Move"}}), do: true
def is_public?(%Activity{data: data}), do: is_public?(data) def public?(%Activity{data: data}), do: public?(data)
def is_public?(%{"directMessage" => true}), do: false def public?(%{"directMessage" => true}), do: false
def is_public?(data) do def public?(data) do
Utils.label_in_message?(Pleroma.Constants.as_public(), data) or Utils.label_in_message?(Pleroma.Constants.as_public(), data) or
Utils.label_in_message?(Utils.as_local_public(), data) Utils.label_in_message?(Utils.as_local_public(), data)
end end
def is_local_public?(%Object{data: data}), do: is_local_public?(data) def local_public?(%Object{data: data}), do: local_public?(data)
def is_local_public?(%Activity{data: data}), do: is_local_public?(data) def local_public?(%Activity{data: data}), do: local_public?(data)
def is_local_public?(data) do def local_public?(data) do
Utils.label_in_message?(Utils.as_local_public(), data) and Utils.label_in_message?(Utils.as_local_public(), data) and
not Utils.label_in_message?(Pleroma.Constants.as_public(), data) not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
end end
def is_private?(activity) do def private?(activity) do
with false <- is_public?(activity), with false <- public?(activity),
%User{follower_address: follower_address} <- %User{follower_address: follower_address} <-
User.get_cached_by_ap_id(activity.data["actor"]) do User.get_cached_by_ap_id(activity.data["actor"]) do
follower_address in activity.data["to"] follower_address in activity.data["to"]
@ -41,20 +41,20 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
end end
end end
def is_announceable?(activity, user, public \\ true) do def announceable?(activity, user, public \\ true) do
is_public?(activity) || public?(activity) ||
(!public && is_private?(activity) && activity.data["actor"] == user.ap_id) (!public && private?(activity) && activity.data["actor"] == user.ap_id)
end end
def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true def direct?(%Activity{data: %{"directMessage" => true}}), do: true
def is_direct?(%Object{data: %{"directMessage" => true}}), do: true def direct?(%Object{data: %{"directMessage" => true}}), do: true
def is_direct?(activity) do def direct?(activity) do
!is_public?(activity) && !is_private?(activity) !public?(activity) && !private?(activity)
end end
def is_list?(%{data: %{"listMessage" => _}}), do: true def list?(%{data: %{"listMessage" => _}}), do: true
def is_list?(_), do: false def list?(_), do: false
@spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean() @spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean()
def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false
@ -77,7 +77,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
when module in [Activity, Object] do when module in [Activity, Object] do
if restrict_unauthenticated_access?(message), if restrict_unauthenticated_access?(message),
do: false, do: false,
else: is_public?(message) and not is_local_public?(message) else: public?(message) and not local_public?(message)
end end
def visible_for_user?(%{__struct__: module} = message, user) def visible_for_user?(%{__struct__: module} = message, user)
@ -86,8 +86,8 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || []) y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || [])
user_is_local = user.local user_is_local = user.local
federatable = not is_local_public?(message) federatable = not local_public?(message)
(is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable) (public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
end end
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do

View File

@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
alias Pleroma.ConfigDB alias Pleroma.ConfigDB
alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update) plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update)
plug( plug(
@ -76,7 +76,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
json(conn, translate_descriptions(descriptions)) json(conn, translate_descriptions(descriptions))
end end
def show(conn, %{only_db: true}) do def show(%{private: %{open_api_spex: %{params: %{only_db: true}}}} = conn, _) do
with :ok <- configurable_from_database() do with :ok <- configurable_from_database() do
configs = Pleroma.Repo.all(ConfigDB) configs = Pleroma.Repo.all(ConfigDB)
@ -128,7 +128,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
end end
end end
def update(%{body_params: %{configs: configs}} = conn, _) do def update(%{private: %{open_api_spex: %{body_params: %{configs: configs}}}} = conn, _) do
with :ok <- configurable_from_database() do with :ok <- configurable_from_database() do
results = results =
configs configs

View File

@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
alias Pleroma.Web.Plugs.InstanceStatic alias Pleroma.Web.Plugs.InstanceStatic
alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
action_fallback(Pleroma.Web.AdminAPI.FallbackController) action_fallback(Pleroma.Web.AdminAPI.FallbackController)
@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :show) plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :show)
plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:update, :delete]) plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:update, :delete])
def show(conn, %{name: document_name}) do def show(%{private: %{open_api_spex: %{params: %{name: document_name}}}} = conn, _) do
with {:ok, url} <- InstanceDocument.get(document_name), with {:ok, url} <- InstanceDocument.get(document_name),
{:ok, content} <- File.read(InstanceStatic.file_path(url)) do {:ok, content} <- File.read(InstanceStatic.file_path(url)) do
conn conn
@ -27,13 +27,18 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
end end
end end
def update(%{body_params: %{file: file}} = conn, %{name: document_name}) do def update(
%{
private: %{open_api_spex: %{body_params: %{file: file}, params: %{name: document_name}}}
} = conn,
_
) do
with {:ok, url} <- InstanceDocument.put(document_name, file.path) do with {:ok, url} <- InstanceDocument.put(document_name, file.path) do
json(conn, %{"url" => url}) json(conn, %{"url" => url})
end end
end end
def delete(conn, %{name: document_name}) do def delete(%{private: %{open_api_spex: %{params: %{name: document_name}}}} = conn, _) do
with :ok <- InstanceDocument.delete(document_name) do with :ok <- InstanceDocument.delete(document_name) do
json(conn, %{}) json(conn, %{})
end end

View File

@ -13,7 +13,7 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
require Logger require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(OAuthScopesPlug, %{scopes: ["admin:read:invites"]} when action == :index) plug(OAuthScopesPlug, %{scopes: ["admin:read:invites"]} when action == :index)
plug( plug(
@ -33,14 +33,14 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
end end
@doc "Create an account registration invite token" @doc "Create an account registration invite token"
def create(%{body_params: params} = conn, _) do def create(%{private: %{open_api_spex: %{body_params: params}}} = conn, _) do
{:ok, invite} = UserInviteToken.create_invite(params) {:ok, invite} = UserInviteToken.create_invite(params)
render(conn, "show.json", invite: invite) render(conn, "show.json", invite: invite)
end end
@doc "Revokes invite by token" @doc "Revokes invite by token"
def revoke(%{body_params: %{token: token}} = conn, _) do def revoke(%{private: %{open_api_spex: %{body_params: %{token: token}}}} = conn, _) do
with {:ok, invite} <- UserInviteToken.find_by_token(token), with {:ok, invite} <- UserInviteToken.find_by_token(token),
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
render(conn, "show.json", invite: updated_invite) render(conn, "show.json", invite: updated_invite)
@ -51,7 +51,13 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
end end
@doc "Sends registration invite via email" @doc "Sends registration invite via email"
def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do def email(
%{
assigns: %{user: user},
private: %{open_api_spex: %{body_params: %{email: email} = params}}
} = conn,
_
) do
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])}, with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])}, {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
{:ok, invite_token} <- UserInviteToken.create_invite(), {:ok, invite_token} <- UserInviteToken.create_invite(),

View File

@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
@cachex Pleroma.Config.get([:cachex, :provider], Cachex) @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
@ -27,7 +27,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation
def index(%{assigns: %{user: _}} = conn, params) do def index(%{assigns: %{user: _}, private: %{open_api_spex: %{params: params}}} = conn, _) do
entries = fetch_entries(params) entries = fetch_entries(params)
urls = paginate_entries(entries, params.page, params.page_size) urls = paginate_entries(entries, params.page, params.page_size)
@ -59,12 +59,19 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
Enum.slice(entries, offset, page_size) Enum.slice(entries, offset, page_size)
end end
def delete(%{assigns: %{user: _}, body_params: %{urls: urls}} = conn, _) do def delete(
%{assigns: %{user: _}, private: %{open_api_spex: %{body_params: %{urls: urls}}}} = conn,
_
) do
MediaProxy.remove_from_banned_urls(urls) MediaProxy.remove_from_banned_urls(urls)
json(conn, %{}) json(conn, %{})
end end
def purge(%{assigns: %{user: _}, body_params: %{urls: urls, ban: ban}} = conn, _) do def purge(
%{assigns: %{user: _}, private: %{open_api_spex: %{body_params: %{urls: urls, ban: ban}}}} =
conn,
_
) do
MediaProxy.Invalidation.purge(urls) MediaProxy.Invalidation.purge(urls)
if ban do if ban do

View File

@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
require Logger require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
@ -31,7 +31,13 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
end end
end end
def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do def follow(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{relay_url: target}}}
} = conn,
_
) do
with {:ok, _message} <- Relay.follow(target) do with {:ok, _message} <- Relay.follow(target) do
ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target}) ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target})
@ -44,7 +50,13 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
end end
end end
def unfollow(%{assigns: %{user: admin}, body_params: %{relay_url: target} = params} = conn, _) do def unfollow(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{relay_url: target} = params}}
} = conn,
_
) do
with {:ok, _message} <- Relay.unfollow(target, %{force: params[:force]}) do with {:ok, _message} <- Relay.unfollow(target, %{force: params[:force]}) do
ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target}) ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target})

View File

@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
require Logger require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show]) plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show])
plug( plug(
@ -31,13 +31,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation
def index(conn, params) do def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do
reports = Utils.get_reports(params, params.page, params.page_size) reports = Utils.get_reports(params, params.page, params.page_size)
render(conn, "index.json", reports: reports) render(conn, "index.json", reports: reports)
end end
def show(conn, %{id: id}) do def show(%{private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do
with %Activity{} = report <- Activity.get_report(id) do with %Activity{} = report <- Activity.get_report(id) do
render(conn, "show.json", Report.extract_report_info(report)) render(conn, "show.json", Report.extract_report_info(report))
else else
@ -45,7 +45,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end end
end end
def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do def update(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{reports: reports}}}
} = conn,
_
) do
result = result =
Enum.map(reports, fn report -> Enum.map(reports, fn report ->
case CommonAPI.update_report_state(report.id, report.state) do case CommonAPI.update_report_state(report.id, report.state) do
@ -73,9 +79,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end end
end end
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{ def notes_create(
id: report_id %{
}) do assigns: %{user: user},
private: %{open_api_spex: %{body_params: %{content: content}, params: %{id: report_id}}}
} = conn,
_
) do
with {:ok, _} <- ReportNote.create(user.id, report_id, content), with {:ok, _} <- ReportNote.create(user.id, report_id, content),
report <- Activity.get_by_id_with_user_actor(report_id) do report <- Activity.get_by_id_with_user_actor(report_id) do
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{
@ -92,10 +102,20 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end end
end end
def notes_delete(%{assigns: %{user: user}} = conn, %{ def notes_delete(
id: note_id, %{
report_id: report_id assigns: %{user: user},
}) do private: %{
open_api_spex: %{
params: %{
id: note_id,
report_id: report_id
}
}
}
} = conn,
_
) do
with {:ok, note} <- ReportNote.destroy(note_id), with {:ok, note} <- ReportNote.destroy(note_id),
report <- Activity.get_by_id_with_user_actor(report_id) do report <- Activity.get_by_id_with_user_actor(report_id) do
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{

View File

@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do
@users_page_size 50 @users_page_size 50
plug(Pleroma.Web.ApiSpec.CastAndValidate) plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
@ -51,13 +51,22 @@ defmodule Pleroma.Web.AdminAPI.UserController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation
def delete(conn, %{nickname: nickname}) do def delete(%{private: %{open_api_spex: %{params: %{nickname: nickname}}}} = conn, _) do
conn conn
|> Map.put(:body_params, %{nicknames: [nickname]}) |> do_deletes([nickname])
|> delete(%{})
end end
def delete(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do def delete(
%{
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
conn
|> do_deletes(nicknames)
end
defp do_deletes(%{assigns: %{user: admin}} = conn, nicknames) when is_list(nicknames) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1) users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
Enum.each(users, fn user -> Enum.each(users, fn user ->
@ -77,9 +86,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
def follow( def follow(
%{ %{
assigns: %{user: admin}, assigns: %{user: admin},
body_params: %{ private: %{
follower: follower_nick, open_api_spex: %{
followed: followed_nick body_params: %{
follower: follower_nick,
followed: followed_nick
}
}
} }
} = conn, } = conn,
_ _
@ -102,9 +115,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
def unfollow( def unfollow(
%{ %{
assigns: %{user: admin}, assigns: %{user: admin},
body_params: %{ private: %{
follower: follower_nick, open_api_spex: %{
followed: followed_nick body_params: %{
follower: follower_nick,
followed: followed_nick
}
}
} }
} = conn, } = conn,
_ _
@ -124,7 +141,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
json(conn, "ok") json(conn, "ok")
end end
def create(%{assigns: %{user: admin}, body_params: %{users: users}} = conn, _) do def create(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{users: users}}}
} = conn,
_
) do
changesets = changesets =
users users
|> Enum.map(fn %{nickname: nickname, email: email, password: password} -> |> Enum.map(fn %{nickname: nickname, email: email, password: password} ->
@ -178,7 +201,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
end end
end end
def show(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do def show(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{params: %{nickname: nickname}}}
} = conn,
_
) do
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
render(conn, "show.json", %{user: user}) render(conn, "show.json", %{user: user})
else else
@ -186,7 +215,11 @@ defmodule Pleroma.Web.AdminAPI.UserController do
end end
end end
def toggle_activation(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do def toggle_activation(
%{assigns: %{user: admin}, private: %{open_api_spex: %{params: %{nickname: nickname}}}} =
conn,
_
) do
user = User.get_cached_by_nickname(nickname) user = User.get_cached_by_nickname(nickname)
{:ok, updated_user} = User.set_activation(user, !user.is_active) {:ok, updated_user} = User.set_activation(user, !user.is_active)
@ -202,7 +235,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "show.json", user: updated_user) render(conn, "show.json", user: updated_user)
end end
def activate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do def activate(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1) users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_activation(users, true) {:ok, updated_users} = User.set_activation(users, true)
@ -212,10 +251,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do
action: "activate" action: "activate"
}) })
render(conn, "index.json", users: Keyword.values(updated_users)) render(conn, "index.json", users: updated_users)
end end
def deactivate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do def deactivate(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1) users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_activation(users, false) {:ok, updated_users} = User.set_activation(users, false)
@ -225,10 +270,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do
action: "deactivate" action: "deactivate"
}) })
render(conn, "index.json", users: Keyword.values(updated_users)) render(conn, "index.json", users: updated_users)
end end
def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do def approve(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1) users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.approve(users) {:ok, updated_users} = User.approve(users)
@ -241,7 +292,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users) render(conn, "index.json", users: updated_users)
end end
def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do def suggest(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1) users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_suggestion(users, true) {:ok, updated_users} = User.set_suggestion(users, true)
@ -254,7 +311,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users) render(conn, "index.json", users: updated_users)
end end
def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do def unsuggest(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1) users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_suggestion(users, false) {:ok, updated_users} = User.set_suggestion(users, false)
@ -267,7 +330,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users) render(conn, "index.json", users: updated_users)
end end
def index(conn, params) do def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do
{page, page_size} = page_params(params) {page, page_size} = page_params(params)
filters = maybe_parse_filters(params[:filters]) filters = maybe_parse_filters(params[:filters])

View File

@ -27,10 +27,12 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
@impl Plug @impl Plug
def call(conn, %{operation_id: operation_id, render_error: render_error}) do def call(conn, %{operation_id: operation_id, render_error: render_error} = opts) do
{spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn) {spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn)
operation = operation_lookup[operation_id] operation = operation_lookup[operation_id]
cast_opts = opts |> Map.take([:replace_params]) |> Map.to_list()
content_type = content_type =
case Conn.get_req_header(conn, "content-type") do case Conn.get_req_header(conn, "content-type") do
[header_value | _] -> [header_value | _] ->
@ -44,7 +46,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
conn = Conn.put_private(conn, :operation_id, operation_id) conn = Conn.put_private(conn, :operation_id, operation_id)
case cast_and_validate(spec, operation, conn, content_type, strict?()) do case cast_and_validate(spec, operation, conn, content_type, strict?(), cast_opts) do
{:ok, conn} -> {:ok, conn} ->
conn conn
@ -94,11 +96,11 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts) def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts)
defp cast_and_validate(spec, operation, conn, content_type, true = _strict) do defp cast_and_validate(spec, operation, conn, content_type, true = _strict, cast_opts) do
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
end end
defp cast_and_validate(spec, operation, conn, content_type, false = _strict) do defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
{:ok, conn} -> {:ok, conn} ->
{:ok, conn} {:ok, conn}
@ -123,7 +125,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
end) end)
conn = %Conn{conn | query_params: query_params} conn = %Conn{conn | query_params: query_params}
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
end end
end end

View File

@ -62,7 +62,7 @@ defmodule Pleroma.Web.ApiSpec.Helpers do
Operation.parameter( Operation.parameter(
:with_relationships, :with_relationships,
:query, :query,
BooleanLike, BooleanLike.schema(),
"Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**" "Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**"
) )
end end

Some files were not shown because too many files have changed in this diff Show More