Merge pull request 'Elixir 1.19 support' (#7758) from gitlab-mr-iid-4381 into develop

Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7758
This commit is contained in:
Phantasm 2026-07-19 22:00:00 +00:00
commit 38bb47b615
55 changed files with 649 additions and 176 deletions

View file

@ -19,8 +19,8 @@ variables:
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh' - 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps: steps:
unit-testing-elixir-1.18: unit-testing-elixir-1.19:
image: elixir:1.18-otp-27-alpine image: elixir:1.19-otp-28-alpine
entrypoint: *script_file_entrypoint entrypoint: *script_file_entrypoint
environment: environment:
MIX_ENV: test MIX_ENV: test

View file

@ -0,0 +1 @@
Implemented ConfigDB configuration blacklist and accompanying Mix task

View file

@ -0,0 +1 @@
Fixed Elixir 1.19 warnings

View file

@ -0,0 +1 @@
Changed how Logger is configured due to changes in Elixir and added deprecation warnings for the old configuration format

View file

@ -133,13 +133,17 @@ config :pleroma, Pleroma.Web.Endpoint,
] ]
# Configures Elixir's Logger # Configures Elixir's Logger
config :logger, backends: [:console] # Primary config
config :logger, level: :debug
config :logger, :console, # Console config
level: :debug, config :logger, :default_handler, level: :debug
config :logger, :default_formatter,
format: "\n$time $metadata[$level] $message\n", format: "\n$time $metadata[$level] $message\n",
metadata: [:actor, :path, :type, :user] metadata: [:actor, :path, :type, :user]
# Syslog config
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :debug, level: :debug,
ident: "pleroma", ident: "pleroma",
@ -962,6 +966,10 @@ config :pleroma, Pleroma.Search.QdrantSearch,
vectors: %{size: 384, distance: "Cosine"} vectors: %{size: 384, distance: "Cosine"}
} }
config :pleroma, :database_config_blacklist, [
{:pleroma, :logger}
]
config :pleroma, :database_config_whitelist, [ config :pleroma, :database_config_whitelist, [
{:pleroma}, {:pleroma},
{:cors_plug}, {:cors_plug},

View file

@ -36,7 +36,7 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local
# different ports. # different ports.
# Do not include timestamps in development logs # Do not include timestamps in development logs
config :logger, Logger.Backends.Console, format: "$metadata[$level] $message\n" config :logger, :default_formatter, format: "$metadata[$level] $message\n"
# Set a higher stacktrace during development. Avoid configuring such # Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive. # in production as building large stacktraces may be expensive.

View file

@ -20,8 +20,7 @@ config :pleroma, Pleroma.Web.Endpoint,
config :phoenix, serve_endpoints: true config :phoenix, serve_endpoints: true
# Do not print debug messages in production # Do not print debug messages in production
config :logger, Logger.Backends.Console, level: :info config :logger, :default_handler, level: :info
config :logger, :console, level: :info
config :logger, :ex_syslogger, level: :info config :logger, :ex_syslogger, level: :info
# ## SSL Support # ## SSL Support

View file

@ -15,9 +15,11 @@ config :pleroma, Pleroma.Captcha,
method: Pleroma.Captcha.Mock method: Pleroma.Captcha.Mock
# Print only warnings and errors during test # Print only warnings and errors during test
config :logger, :console, config :logger, :default_handler, level: :warning
level: :warning,
format: "\n[$level] $message\n" config :logger, :default_formatter,
format: "\n[$level] $message\n",
colors: [enabled: false]
config :pleroma, :auth, oauth_consumer_strategies: [] config :pleroma, :auth, oauth_consumer_strategies: []

View file

@ -858,21 +858,23 @@ Web Push Notifications configuration. You can use the mix task `mix web_push.gen
* ``private_key``: VAPID private key * ``private_key``: VAPID private key
## :logger ## :logger
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog * Logging to console/stdout is done by default, use `{ExSyslogger, :ex_syslogger}` to log to syslog
An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed: An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
```elixir ```elixir
config :logger, config :pleroma, :logger,
backends: [{ExSyslogger, :ex_syslogger}] backends: [{ExSyslogger, :ex_syslogger}]
config :logger, default_handler: false
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :warning level: :warning
``` ```
Another example, keeping console output and adding the pid to syslog output: Another example, keeping console output and adding the pid to syslog output:
```elixir ```elixir
config :logger, config :pleroma, :logger,
backends: [:console, {ExSyslogger, :ex_syslogger}] backends: [{ExSyslogger, :ex_syslogger}]
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :warning, level: :warning,
@ -883,23 +885,22 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s
An example of logging info to local syslog, but debug to console: An example of logging info to local syslog, but debug to console:
```elixir ```elixir
config :logger, config :pleroma, :logger,
backends: [ {ExSyslogger, :ex_syslogger}, :console ], backends: [{ExSyslogger, :ex_syslogger}]
level: :info
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :info, level: :info,
ident: "pleroma", ident: "pleroma",
format: "$metadata[$level] $message" format: "$metadata[$level] $message"
config :logger, :console, config :logger, :default_handler,
level: :debug, level: :debug
config :logger, :default_formatter,
format: "\n$time $metadata[$level] $message\n", format: "\n$time $metadata[$level] $message\n",
metadata: [:request_id] metadata: [:request_id]
``` ```
## Database options ## Database options
### RUM indexing for full text search ### RUM indexing for full text search

View file

@ -1,8 +1,8 @@
## Required dependencies ## Required dependencies
* PostgreSQL >=11.0 * PostgreSQL >=11.0
* Elixir >=1.15.0 <1.19 * Elixir >=1.15.0, <1.20
* Erlang OTP >=23.0.0 (supported: <28) * Erlang OTP >=24.0.0 (supported: <29)
* git * git
* file / libmagic * file / libmagic
* gcc or clang * gcc or clang

View file

@ -252,40 +252,43 @@ defmodule Mix.Tasks.Pleroma.Config do
if whitelisted_configs in [nil, false] do if whitelisted_configs in [nil, false] do
shell_error("No unwanted settings in ConfigDB. No changes made.") shell_error("No unwanted settings in ConfigDB. No changes made.")
else else
whitelisted_groups = {whitelisted_groups, whitelisted_keys} = get_filters(whitelisted_configs)
whitelisted_configs
|> Enum.filter(fn
{_group} -> true
_ -> false
end)
|> Enum.map(fn {group} -> group end)
whitelisted_keys =
whitelisted_configs
|> Enum.filter(fn
{_group, _key} -> true
_ -> false
end)
filtered = filtered =
from(c in ConfigDB) from(c in ConfigDB)
|> Repo.all() |> Repo.all()
|> Enum.filter(&not_whitelisted?(&1, whitelisted_groups, whitelisted_keys)) |> Enum.filter(&not_whitelisted?(&1, whitelisted_groups, whitelisted_keys))
if not Enum.empty?(filtered) do do_config_filter(filtered, force)
shell_info("The following settings will be removed from ConfigDB:\n") end
Enum.each(filtered, &dump(&1)) end
if force or shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do # Removes blacklisted configuration sections
filtered_ids = Enum.map(filtered, fn %{id: id} -> id end) def run(["filter_blacklisted" | rest]) do
{options, [], []} =
OptionParser.parse(
rest,
strict: [force: :boolean],
aliases: [f: :force]
)
Repo.delete_all(from(c in ConfigDB, where: c.id in ^filtered_ids)) force = Keyword.get(options, :force, false)
else
shell_error("No changes made.") start_pleroma()
end
else blacklisted_configs = Pleroma.Config.get(:database_config_blacklist)
shell_error("No unwanted settings in ConfigDB. No changes made.")
end if blacklisted_configs in [nil, false] do
shell_error("No unwanted settings in ConfigDB. No changes made.")
else
{blacklisted_groups, blacklisted_keys} = get_filters(blacklisted_configs)
filtered =
from(c in ConfigDB)
|> Repo.all()
|> Enum.filter(&blacklisted?(&1, blacklisted_groups, blacklisted_keys))
do_config_filter(filtered, force)
end end
end end
@ -490,8 +493,49 @@ defmodule Mix.Tasks.Pleroma.Config do
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;") Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
end end
defp get_filters(config) do
groups =
config
|> Enum.filter(fn
{_group} -> true
_ -> false
end)
|> Enum.map(fn {group} -> group end)
keys =
config
|> Enum.filter(fn
{_group, _key} -> true
_ -> false
end)
{groups, keys}
end
defp not_whitelisted?(%{group: group, key: key}, whitelisted_groups, whitelisted_keys) do defp not_whitelisted?(%{group: group, key: key}, whitelisted_groups, whitelisted_keys) do
not Enum.member?(whitelisted_groups, group) and not Enum.member?(whitelisted_groups, group) and
not Enum.member?(whitelisted_keys, {group, key}) not Enum.member?(whitelisted_keys, {group, key})
end end
defp blacklisted?(%{group: group, key: key}, blacklisted_groups, blacklisted_keys) do
Enum.member?(blacklisted_groups, group) or
Enum.member?(blacklisted_keys, {group, key})
end
defp do_config_filter(filtered, _) when length(filtered) == 0 do
shell_error("No unwanted settings in ConfigDB. No changes made.")
end
defp do_config_filter(filtered, force) do
shell_info("The following settings will be removed from ConfigDB:\n")
Enum.each(filtered, &dump(&1))
if force or shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
filtered_ids = Enum.map(filtered, fn %{id: id} -> id end)
Repo.delete_all(from(c in ConfigDB, where: c.id in ^filtered_ids))
else
shell_error("No changes made.")
end
end
end end

View file

@ -43,6 +43,7 @@ defmodule Pleroma.Application do
# every time the application is restarted, so we disable module # every time the application is restarted, so we disable module
# conflicts at runtime # conflicts at runtime
Code.compiler_options(ignore_module_conflict: true) Code.compiler_options(ignore_module_conflict: true)
configure_logger()
Pleroma.Telemetry.Logger.attach() Pleroma.Telemetry.Logger.attach()
Config.Holder.save_default() Config.Holder.save_default()
Pleroma.HTML.compile_scrubbers() Pleroma.HTML.compile_scrubbers()
@ -113,6 +114,44 @@ defmodule Pleroma.Application do
Supervisor.start_link(children, opts) Supervisor.start_link(children, opts)
end end
def configure_logger do
Config.get([:logger, :backends], [])
|> Enum.each(fn backend ->
case backend_to_logger(backend) do
{:ok, logger} ->
add_logger(logger)
{:error, :console_backend} ->
Logger.warning(":console is no longer considered a backend and is enabled by default")
end
end)
end
defp add_logger(backend) do
case LoggerBackends.add(backend) do
{:ok, _} ->
Logger.debug("Successfully added logger backend: #{inspect(backend)}")
{:error, :already_present} ->
:ok
{:error, reason} ->
Logger.error("Failed to add logger backend #{inspect(backend)}: #{inspect(reason)}")
end
end
defp backend_to_logger(:console), do: {:error, :console_backend}
defp backend_to_logger({:ex_syslogger = backend, name}) do
Logger.warning(
"Configuration {:#{backend}, :#{name}} is incorrect. Use {ExSyslogger, :#{name}} instead!"
)
{:ok, {ExSyslogger, name}}
end
defp backend_to_logger(backend), do: {:ok, backend}
def load_custom_modules do def load_custom_modules do
dir = Config.get([:modules, :runtime_dir]) dir = Config.get([:modules, :runtime_dir])

View file

@ -20,6 +20,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
] ]
@logger_formatter_config_knobs [:colors, :format, :metadata, :truncate, :utc_log]
def check_exiftool_filter do def check_exiftool_filter do
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
@ -218,7 +220,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
check_quarantined_instances_tuples(), check_quarantined_instances_tuples(),
check_transparency_exclusions_tuples(), check_transparency_exclusions_tuples(),
check_simple_policy_tuples(), check_simple_policy_tuples(),
check_exiftool_filter() check_exiftool_filter(),
check_deprecated_logger_config()
] ]
|> Enum.reduce(:ok, fn |> Enum.reduce(:ok, fn
:ok, :ok -> :ok :ok, :ok -> :ok
@ -415,4 +418,72 @@ defmodule Pleroma.Config.DeprecationWarnings do
:ok :ok
end end
end end
defp merge_deprecated_logger_config(config) do
formatter_config = Enum.filter(config, fn {k, _} -> k in @logger_formatter_config_knobs end)
formatter = Logger.default_formatter(formatter_config)
log_level = Keyword.get(config, :level, nil)
Logger.debug("""
Reconfiguring console Logger with deprecated configuration syntax.
Handler configuration:
log_level: #{inspect(log_level)}
Formatter:
#{inspect(formatter)}
""")
if Config.get(:env) != :test do
if log_level, do: :logger.update_handler_config(:default, :level, log_level)
:logger.set_handler_config(:default, :formatter, formatter)
end
end
@spec check_deprecated_logger_config() :: :ok | :error
def check_deprecated_logger_config do
backends_config = Application.get_env(:logger, :backends)
console_config = Application.get_env(:logger, :console)
# NOTE: No need to merge the old backends config since it still works.
# And new configuration will just add new Logger backends.
backend =
if backends_config do
Logger.warning("""
!!!DEPRECATION WARNING!!!
Your configuration is using deprecated syntax for configuring backends of Elixir's logger.
`config :logger, backends: [...]` is deprecated syntax due to changes in Elixir.
Please update your configuration at your earliest convenience to use:
`config :pleroma, :logger, backends: [...]`
Note: `:console` is no longer considered a backend and is used by default, you can disable it using:
`config :logger, :default_handler: false`
""")
true
else
false
end
console =
if console_config do
Logger.warning("""
!!!DEPRECATION WARNING!!!
Your configuration is using deprecated syntax for configuring logging to console.
`config :logger, :console` is deprecated syntax due to changes in Elixir.
Please update your configuration at your earliest convenience to use
`config :logger, default_handler` and `config :logger, :default_formatter`.
Note: `:default_handler` is used only for the `level` setting. All other configurations go under
`:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility
""")
merge_deprecated_logger_config(console_config)
true
else
false
end
if backend or console, do: :error, else: :ok
end
end end

View file

@ -51,7 +51,7 @@ defmodule Pleroma.Config.TransferTask do
started_applications = Application.started_applications() started_applications = Application.started_applications()
reject = [nil, :postgrex] reject = [nil, :postgrex, :logger]
reject = reject =
if restart_pleroma? do if restart_pleroma? do

View file

@ -12,13 +12,27 @@ defmodule Pleroma.Docs.Generator do
@spec list_behaviour_implementations(behaviour :: module()) :: [module()] @spec list_behaviour_implementations(behaviour :: module()) :: [module()]
def list_behaviour_implementations(behaviour) do def list_behaviour_implementations(behaviour) do
:code.all_loaded() # :code.all_loaded() no longer works on Elixir 1.19 and newer since modules are lazily loaded.
|> Enum.filter(fn {module, _} -> # We can get a complete list of modules by interating over Application.loaded_applications/0,
# but that includes every module which would get loaded below, which is very slow.
# Hardcode the Pleroma application instead, which is good enough for now.
application_modules =
case :application.get_key(:pleroma, :modules) do
{:ok, modules} -> modules
:undefined -> []
end
# At compile time or in tests some modules will be outside of the Pleroma application.
loaded_modules = Enum.map(:code.all_loaded(), fn {module, _} -> module end)
(application_modules ++ loaded_modules)
|> Enum.uniq()
|> Enum.filter(fn module ->
Code.ensure_loaded(module)
# This shouldn't be needed as all modules are expected to have module_info/1, # This shouldn't be needed as all modules are expected to have module_info/1,
# but in test environments some transient modules `:elixir_compiler_XX` # but in test environments some transient modules `:elixir_compiler_XX`
# are loaded for some reason (where XX is a random integer). # are loaded for some reason (where XX is a random integer).
Code.ensure_loaded(module)
if function_exported?(module, :module_info, 1) do if function_exported?(module, :module_info, 1) do
module.module_info(:attributes) module.module_info(:attributes)
|> Keyword.get_values(:behaviour) |> Keyword.get_values(:behaviour)
@ -26,7 +40,6 @@ defmodule Pleroma.Docs.Generator do
|> Enum.member?(behaviour) |> Enum.member?(behaviour)
end end
end) end)
|> Enum.map(fn {module, _} -> module end)
end end
@doc """ @doc """

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
@moduledoc "Adds expiration to all local Create activities" @moduledoc "Adds expiration to all local Create activities"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(activity) do def filter(activity) do
activity = activity =

View file

@ -3,11 +3,11 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
@moduledoc "Prevent followbots from following with a bit of heuristic"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.User alias Pleroma.User
@moduledoc "Prevent followbots from following with a bit of heuristic" use Pleroma.Web.ActivityPub.MRF.Policy
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
# XXX: this should become User.normalize_by_ap_id() or similar, really. # XXX: this should become User.normalize_by_ap_id() or similar, really.
defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id) defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id)

View file

@ -3,9 +3,11 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.User alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
require Logger require Logger

View file

@ -3,11 +3,13 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do defmodule Pleroma.Web.ActivityPub.MRF.AntiMentionSpamPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
require Pleroma.Constants require Pleroma.Constants
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
defp user_has_posted?(%User{} = u), do: u.note_count > 0 defp user_has_posted?(%User{} = u), do: u.note_count > 0

View file

@ -3,10 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
require Logger
@moduledoc "Drop and log everything received" @moduledoc "Drop and log everything received"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Logger
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(activity) do def filter(activity) do
Logger.debug("REJECTING #{inspect(activity)}") Logger.debug("REJECTING #{inspect(activity)}")

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Object.Updater alias Pleroma.Object.Updater
@ -10,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicy do
@moduledoc "Reject or force-unlisted emojis with certain URLs or names" @moduledoc "Reject or force-unlisted emojis with certain URLs or names"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
defp config_remove_url do defp config_remove_url do
Pleroma.Config.get([:mrf_emoji, :remove_url], []) Pleroma.Config.get([:mrf_emoji, :remove_url], [])

View file

@ -3,11 +3,13 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
alias Pleroma.Object
@moduledoc "Ensure a re: is prepended on replies to a post with a Subject" @moduledoc "Ensure a re: is prepended on replies to a post with a Subject"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Object
use Pleroma.Web.ActivityPub.MRF.Policy
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless]) @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
def history_awareness, do: :auto def history_awareness, do: :auto

View file

@ -4,12 +4,15 @@
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
require Logger require Logger
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(activity) do def filter(activity) do
with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]), with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]),

View file

@ -3,9 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
@moduledoc "Remove bot posts from federated timeline" @moduledoc "Remove bot posts from federated timeline"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.User
use Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants

View file

@ -3,13 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ForceMention do defmodule Pleroma.Web.ActivityPub.MRF.ForceMention do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
defp get_author(url) do defp get_author(url) do
with %Object{data: %{"actor" => actor}} <- Object.normalize(url, fetch: false), with %Object{data: %{"actor" => actor}} <- Object.normalize(url, fetch: false),

View file

@ -3,13 +3,15 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Formatter alias Pleroma.Formatter
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def history_awareness, do: :auto def history_awareness, do: :auto

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Config alias Pleroma.Config
@ -14,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists. Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
""" """
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def history_awareness, do: :manual def history_awareness, do: :manual

View file

@ -3,13 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
@moduledoc "Block activities with too much mentions (configurable)"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.User alias Pleroma.User
require Pleroma.Constants require Pleroma.Constants
@moduledoc "Block activities with too much mentions (configurable)" use Pleroma.Web.ActivityPub.MRF.Policy
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp delist_activity(activity, threshold) when threshold > 0 do defp delist_activity(activity, threshold) when threshold > 0 do
follower_collection = User.get_cached_by_ap_id(activity["actor"]).follower_address follower_collection = User.get_cached_by_ap_id(activity["actor"]).follower_address

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
@moduledoc "Force a quote line into the message content." @moduledoc "Force a quote line into the message content."
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
defp build_inline_quote(template, url) do defp build_inline_quote(template, url) do
quote_line = String.replace(template, "{url}", "<a href=\"#{url}\">#{url}</a>") quote_line = String.replace(template, "{url}", "<a href=\"#{url}\">#{url}</a>")

View file

@ -3,13 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
@moduledoc "Reject or Word-Replace activities with a keyword or regex"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Web.ActivityPub.MRF.Utils alias Pleroma.Web.ActivityPub.MRF.Utils
@moduledoc "Reject or Word-Replace activities with a keyword or regex" use Pleroma.Web.ActivityPub.MRF.Policy
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
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)

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
@moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them" @moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.HTTP alias Pleroma.HTTP
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy

View file

@ -4,9 +4,10 @@
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
@moduledoc "Block activities which mention a user" @moduledoc "Block activities which mention a user"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(%{"type" => "Create"} = activity) do def filter(%{"type" => "Create"} = activity) do
reject_actors = Pleroma.Config.get([:mrf_mention, :actors], []) reject_actors = Pleroma.Config.get([:mrf_mention, :actors], [])

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
@moduledoc "Filter local activities which have no content" @moduledoc "Filter local activities which have no content"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
@impl true @impl true

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
@moduledoc "Does nothing (lets the messages go through unmodified)" @moduledoc "Does nothing (lets the messages go through unmodified)"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(activity) do def filter(activity) do
{:ok, activity} {:ok, activity}

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
@moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)" @moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def history_awareness, do: :auto def history_awareness, do: :auto

View file

@ -4,9 +4,11 @@
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@moduledoc "Scrub configured hypertext markup" @moduledoc "Scrub configured hypertext markup"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.HTML alias Pleroma.HTML
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def history_awareness, do: :auto def history_awareness, do: :auto

View file

@ -38,6 +38,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do
- `unlist`: Unlist all detected NSFW content? Default: `false` - `unlist`: Unlist all detected NSFW content? Default: `false`
- `reject`: Reject all detected NSFW content (takes precedence)? Default: `false` - `reject`: Reject all detected NSFW content (takes precedence)? Default: `false`
""" """
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Constants alias Pleroma.Constants
alias Pleroma.HTTP alias Pleroma.HTTP
@ -46,7 +48,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NsfwApiPolicy do
require Logger require Logger
require Pleroma.Constants require Pleroma.Constants
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
@policy :mrf_nsfw_api @policy :mrf_nsfw_api
def build_request_url(url) do def build_request_url(url) do

View file

@ -3,13 +3,15 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
@moduledoc "Filter activities depending on their age"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
require Pleroma.Constants require Pleroma.Constants
@moduledoc "Filter activities depending on their age" use Pleroma.Web.ActivityPub.MRF.Policy
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp check_date(%{"object" => %{"published" => published}} = activity) do defp check_date(%{"object" => %{"published" => published}} = activity) do
with %DateTime{} = now <- DateTime.utc_now(), with %DateTime{} = now <- DateTime.utc_now(),

View file

@ -15,4 +15,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do
} }
@callback history_awareness() :: :auto | :manual @callback history_awareness() :: :auto | :manual
@optional_callbacks config_description: 0, history_awareness: 0, id_filter: 1 @optional_callbacks config_description: 0, history_awareness: 0, id_filter: 1
defmacro __using__(_) do
quote do
# Otherwise Pleroma.Docs.Generator.list_behaviour_implementations fails
# since the Elixir compiler doesn't automatically load the module since v1.19.
@compile {:autoload, true}
end
end
end end

View file

@ -6,11 +6,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuietReply do
@moduledoc """ @moduledoc """
QuietReply alters the scope of activities from local users when replying by enforcing them to be "Unlisted" or "Quiet Public". This delivers the activity to all the expected recipients and instances, but it will not be published in the Federated / The Whole Known Network timelines. It will still be published to the Home timelines of the user's followers and visible to anyone who opens the thread. QuietReply alters the scope of activities from local users when replying by enforcing them to be "Unlisted" or "Quiet Public". This delivers the activity to all the expected recipients and instances, but it will not be published in the Federated / The Whole Known Network timelines. It will still be published to the Home timelines of the user's followers and visible to anyone who opens the thread.
""" """
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.User alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def history_awareness, do: :auto def history_awareness, do: :auto

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do
@moduledoc "Force a Link tag for posts quoting another post. (may break outgoing federation of quote posts with older Pleroma versions)" @moduledoc "Force a Link tag for posts quoting another post. (may break outgoing federation of quote posts with older Pleroma versions)"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
require Pleroma.Constants require Pleroma.Constants

View file

@ -4,11 +4,12 @@
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
@moduledoc "Rejects non-public (followers-only, direct) activities" @moduledoc "Rejects non-public (followers-only, direct) activities"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants

View file

@ -2,6 +2,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
@moduledoc "Drop remote reports if they don't contain enough information." @moduledoc "Drop remote reports if they don't contain enough information."
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
@impl true @impl true

View file

@ -6,6 +6,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
@moduledoc "Filter activities depending on their origin instance" @moduledoc "Filter activities depending on their origin instance"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.FollowingRelationship alias Pleroma.FollowingRelationship
alias Pleroma.User alias Pleroma.User

View file

@ -3,12 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
@moduledoc "Detect new emojis by their shortcode and steals them"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Logger require Logger
alias Pleroma.Config alias Pleroma.Config
@moduledoc "Detect new emojis by their shortcode and steals them" use Pleroma.Web.ActivityPub.MRF.Policy
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], []) defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], [])

View file

@ -3,12 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Web.ActivityPub.MRF alias Pleroma.Web.ActivityPub.MRF
require Logger require Logger
@behaviour Pleroma.Web.ActivityPub.MRF.Policy use Pleroma.Web.ActivityPub.MRF.Policy
defp lookup_subchain(actor) do defp lookup_subchain(actor) do
with matches <- Config.get([:mrf_subchain, :match_actor]), with matches <- Config.get([:mrf_subchain, :match_actor]),

View file

@ -3,8 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
@moduledoc """ @moduledoc """
Apply policies based on user tags Apply policies based on user tags
@ -18,6 +16,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
- `mrf_tag:disable-remote-subscription`: Reject non-local follow requests - `mrf_tag:disable-remote-subscription`: Reject non-local follow requests
- `mrf_tag:disable-any-subscription`: Reject any follow requests - `mrf_tag:disable-any-subscription`: Reject any follow requests
""" """
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.User
use Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants require Pleroma.Constants

View file

@ -3,11 +3,13 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
alias Pleroma.Config
@moduledoc "Accept-list of users from specified instances" @moduledoc "Accept-list of users from specified instances"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config
use Pleroma.Web.ActivityPub.MRF.Policy
defp filter_by_list(activity, []), do: {:ok, activity} defp filter_by_list(activity, []), do: {:ok, activity}
defp filter_by_list(%{"actor" => actor} = activity, allow_list) do defp filter_by_list(%{"actor" => actor} = activity, allow_list) do

View file

@ -4,9 +4,10 @@
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
@moduledoc "Filter activities which belong to certain activity vocabularies" @moduledoc "Filter activities which belong to certain activity vocabularies"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy @behaviour Pleroma.Web.ActivityPub.MRF.Policy
use Pleroma.Web.ActivityPub.MRF.Policy
@impl true @impl true
def filter(%{"type" => "Undo", "object" => object} = activity) do def filter(%{"type" => "Undo", "object" => object} = activity) do
with {:ok, _} <- filter(object) do with {:ok, _} <- filter(object) do

View file

@ -71,7 +71,10 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
end end
def descriptions(conn, _params) do def descriptions(conn, _params) do
descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1) descriptions =
Pleroma.Docs.JSON.compiled_descriptions()
|> Enum.filter(&allowed_config?(&1, :whitelist))
|> Enum.reject(&allowed_config?(&1, :blacklist))
json(conn, translate_descriptions(descriptions)) json(conn, translate_descriptions(descriptions))
end end
@ -132,7 +135,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
with :ok <- configurable_from_database() do with :ok <- configurable_from_database() do
results = results =
configs configs
|> Enum.filter(&whitelisted_config?/1) |> Enum.filter(&allowed_config?(&1, :whitelist))
|> Enum.reject(&allowed_config?(&1, :blacklist))
|> Enum.map(fn |> Enum.map(fn
%{group: group, key: key, delete: true} = params -> %{group: group, key: key, delete: true} = params ->
ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]}) ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]})
@ -174,27 +178,34 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
end end
end end
defp whitelisted_config?(":pleroma", ":database_config_whitelist"), do: false defp allowed_config?(":pleroma", ":database_config_whitelist", _), do: true
defp allowed_config?(":pleroma", ":database_config_blacklist", _), do: true
defp whitelisted_config?(group, key) do defp allowed_config?(group, key, mode) do
if whitelisted_configs = Config.get(:database_config_whitelist) do configs =
Enum.any?(whitelisted_configs, fn case mode do
{whitelisted_group} -> :whitelist -> Config.get(:database_config_whitelist)
group == inspect(whitelisted_group) :blacklist -> Config.get(:database_config_blacklist)
end
{whitelisted_group, whitelisted_key} -> if configs do
group == inspect(whitelisted_group) && key == inspect(whitelisted_key) Enum.any?(configs, fn
{allowed_group} ->
group == inspect(allowed_group)
{allowed_group, allowed_key} ->
group == inspect(allowed_group) && key == inspect(allowed_key)
end) end)
else else
true true
end end
end end
defp whitelisted_config?(%{group: group, key: key}) do defp allowed_config?(%{group: group, key: key}, mode) do
whitelisted_config?(group, key) allowed_config?(group, key, mode)
end end
defp whitelisted_config?(%{group: group} = config) do defp allowed_config?(%{group: group} = config, mode) do
whitelisted_config?(group, config[:key]) allowed_config?(group, config[:key], mode)
end end
end end

View file

@ -15,6 +15,13 @@ defmodule Pleroma.Mixfile do
aliases: aliases(), aliases: aliases(),
deps: deps(), deps: deps(),
test_coverage: [tool: :covertool, summary: true], test_coverage: [tool: :covertool, summary: true],
test_ignore_filters: [
"test/credo/check/consistency/file_location.ex",
"test/fixtures/config/temp.exported_from_db.secret.exs",
"test/fixtures/config/temp.secret.exs",
"test/fixtures/modules/good_mrf.ex",
"test/fixtures/modules/runtime_module.ex"
],
# Docs # Docs
name: "Pleroma", name: "Pleroma",
homepage_url: "https://pleroma.social/", homepage_url: "https://pleroma.social/",

View file

@ -369,5 +369,39 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
MixTask.run(["filter_whitelisted", "--force"]) MixTask.run(["filter_whitelisted", "--force"])
assert config_records() == existing assert config_records() == existing
end end
test "filters blacklisted settings" do
clear_config(:database_config_whitelist, [{:pleroma}])
clear_config(:database_config_blacklist, [{:pleroma, :logger}, {:blacklisted_group}])
insert_config_record(:pleroma, :logger, backends: :console)
insert_config_record(:web_push_encryption, :vapid_details, a: 1)
insert_config_record(:blacklisted_group, :anything, a: 1)
MixTask.run(["filter_blacklisted", "--force"])
assert [
%ConfigDB{group: :pleroma, key: :instance},
%ConfigDB{group: :pleroma, key: Pleroma.Captcha},
%ConfigDB{group: :pleroma2, key: :key2},
%ConfigDB{group: :web_push_encryption, key: :vapid_details}
] = config_records()
end
test "filter_blacklisted doesn't crash when blacklist is unset" do
clear_config(:database_config_blacklist, nil)
existing = config_records()
MixTask.run(["filter_blacklisted", "--force"])
assert config_records() == existing
end
test "filter_blacklisted doesn't crash when blacklist is disabled" do
clear_config(:database_config_blacklist, false)
existing = config_records()
MixTask.run(["filter_blacklisted", "--force"])
assert config_records() == existing
end
end end
end end

View file

@ -0,0 +1,39 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ApplicationTest do
use Pleroma.DataCase
import ExUnit.CaptureLog
# TODO HACK: Temporarily disabled.
# Currently causes a bunch of warnings about :warn being deprecated in logs.
# ex_syslogger needs to either be updated upstream or forked.
describe "config :pleroma, :logger, backends: [{:ex_syslogger, :ex_syslogger}]" do
setup do
clear_config([:logger], backends: [{:ex_syslogger, :ex_syslogger}])
end
@tag :skip
test "is handled" do
log = capture_log(fn -> Pleroma.Application.configure_logger() end)
assert log =~
"Configuration {:ex_syslogger, :ex_syslogger} is incorrect. Use {ExSyslogger, :ex_syslogger} instead!"
assert log =~ "Successfully added logger backend: {ExSyslogger, :ex_syslogger}"
end
end
describe "config :pleroma, :logger, :backends: [:console]" do
setup do
clear_config([:logger], backends: [:console])
end
test "emits a warning" do
assert capture_log(fn -> Pleroma.Application.configure_logger() end) =~
":console is no longer considered a backend and is enabled by default"
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.DeprecationWarningsTest do defmodule Pleroma.Config.DeprecationWarningsTest do
use ExUnit.Case use ExUnit.Case, async: false
use Pleroma.Tests.Helpers use Pleroma.Tests.Helpers
import ExUnit.CaptureLog import ExUnit.CaptureLog
@ -388,4 +388,62 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
end) =~ end) =~
"Your config is using the old namespace for the Shoutbox configuration." "Your config is using the old namespace for the Shoutbox configuration."
end end
describe "check_deprecated_logger_config" do
setup do
initial_console = Application.get_env(:logger, :console, nil)
initial_backends = Application.get_env(:logger, :backends, nil)
# NOTE: The Logger does not get reconfigured when running in the test env,
# the added and on_exit removed settings below should have no effect besides
# triggering the deprecation warnings
Application.put_env(:logger, :console, level: :all)
Application.put_env(:logger, :backends, [:console])
on_exit(fn ->
if initial_console do
Application.put_env(:logger, :console, initial_console)
else
Application.delete_env(:logger, :console)
end
if initial_backends do
Application.put_env(:logger, :backends, initial_backends)
else
Application.delete_env(:logger, :backends)
end
end)
end
test "warns on deprecated syntax" do
log =
capture_log(fn ->
Pleroma.Config.DeprecationWarnings.check_deprecated_logger_config()
end)
assert log =~
"""
!!!DEPRECATION WARNING!!!
Your configuration is using deprecated syntax for configuring backends of Elixir's logger.
`config :logger, backends: [...]` is deprecated syntax due to changes in Elixir.
Please update your configuration at your earliest convenience to use:
`config :pleroma, :logger, backends: [...]`
Note: `:console` is no longer considered a backend and is used by default, you can disable it using:
`config :logger, :default_handler: false`
"""
assert log =~
"""
!!!DEPRECATION WARNING!!!
Your configuration is using deprecated syntax for configuring logging to console.
`config :logger, :console` is deprecated syntax due to changes in Elixir.
Please update your configuration at your earliest convenience to use
`config :logger, default_handler` and `config :logger, :default_formatter`.
Note: `:default_handler` is used only for the `level` setting. All other configurations go under
`:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility
"""
end
end
end end

View file

@ -194,19 +194,9 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
setup do: clear_config(:configurable_from_database, true) setup do: clear_config(:configurable_from_database, true)
setup do:
clear_config(:database_config_whitelist, [
{:pleroma},
{:http},
{:idna},
{:oban},
{:tesla},
{:ueberauth}
])
test "create new config setting in db", %{conn: conn} do test "create new config setting in db", %{conn: conn} do
ueberauth = Application.get_env(:ueberauth, Ueberauth) aws = Application.get_env(:ex_aws, :s3)
on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end) on_exit(fn -> Application.put_env(:ex_aws, :s3, aws) end)
conn = conn =
conn conn
@ -215,9 +205,9 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
configs: [ configs: [
%{group: ":pleroma", key: ":key1", value: "value1"}, %{group: ":pleroma", key: ":key1", value: "value1"},
%{ %{
group: ":ueberauth", group: ":cors_plug",
key: "Ueberauth", key: ":key1",
value: [%{"tuple" => [":consumer_secret", "aaaa"]}] value: [%{"tuple" => [":key", "value"]}]
}, },
%{ %{
group: ":pleroma", group: ":pleroma",
@ -244,9 +234,9 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"} value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
}, },
%{ %{
group: ":idna", group: ":ex_aws",
key: ":key5", key: ":s3",
value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} value: %{"tuple" => ["access_key_id", "aaaa", []]}
} }
] ]
}) })
@ -260,10 +250,10 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
"db" => [":key1"] "db" => [":key1"]
}, },
%{ %{
"group" => ":ueberauth", "group" => ":cors_plug",
"key" => "Ueberauth", "key" => ":key1",
"value" => [%{"tuple" => [":consumer_secret", "aaaa"]}], "value" => [%{"tuple" => [":key", "value"]}],
"db" => [":consumer_secret"] "db" => [":key"]
}, },
%{ %{
"group" => ":pleroma", "group" => ":pleroma",
@ -293,10 +283,10 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
"db" => [":key4"] "db" => [":key4"]
}, },
%{ %{
"group" => ":idna", "group" => ":ex_aws",
"key" => ":key5", "key" => ":s3",
"value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}, "value" => %{"tuple" => ["access_key_id", "aaaa", []]},
"db" => [":key5"] "db" => [":s3"]
} }
], ],
"need_reboot" => false "need_reboot" => false
@ -322,18 +312,19 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
nested_5: :upload nested_5: :upload
} }
assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} assert Application.get_env(:cors_plug, :key1) == [key: "value"]
assert Application.get_env(:ex_aws, :s3) == {"access_key_id", "aaaa", []}
end end
test "save configs setting without explicit key", %{conn: conn} do test "save configs setting without explicit key", %{conn: conn} do
adapter = Application.get_env(:http, :adapter) mime = Application.get_env(:mime, :library)
send_user_agent = Application.get_env(:http, :send_user_agent) prometheus = Application.get_env(:prometheus, Pleroma.Web.Endpoint.MetricsExporter)
user_agent = Application.get_env(:http, :user_agent) web_push = Application.get_env(:web_push_encryption, :vapid_details)
on_exit(fn -> on_exit(fn ->
Application.put_env(:http, :adapter, adapter) Application.put_env(:mime, :library, mime)
Application.put_env(:http, :send_user_agent, send_user_agent) Application.put_env(:prometheus, Pleroma.Web.Endpoint.MetricsExporter, prometheus)
Application.put_env(:http, :user_agent, user_agent) Application.put_env(:web_push_encryption, :vapid_details, web_push)
end) end)
conn = conn =
@ -342,19 +333,19 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|> post("/api/pleroma/admin/config", %{ |> post("/api/pleroma/admin/config", %{
configs: [ configs: [
%{ %{
group: ":http", group: ":mime",
key: ":adapter", key: ":library",
value: [":someval"] value: [":someval"]
}, },
%{ %{
group: ":http", group: ":prometheus",
key: ":send_user_agent", key: "Pleroma.Web.Endpoint.MetricsExporter",
value: true value: true
}, },
%{ %{
group: ":http", group: ":web_push_encryption",
key: ":user_agent", key: ":vapid_details",
value: [":default"] value: [":nope"]
} }
] ]
}) })
@ -362,30 +353,30 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert json_response_and_validate_schema(conn, 200) == %{ assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [ "configs" => [
%{ %{
"group" => ":http", "group" => ":mime",
"key" => ":adapter", "key" => ":library",
"value" => [":someval"], "value" => [":someval"],
"db" => [":adapter"] "db" => [":library"]
}, },
%{ %{
"group" => ":http", "group" => ":prometheus",
"key" => ":send_user_agent", "key" => "Pleroma.Web.Endpoint.MetricsExporter",
"value" => true, "value" => true,
"db" => [":send_user_agent"] "db" => ["Pleroma.Web.Endpoint.MetricsExporter"]
}, },
%{ %{
"group" => ":http", "group" => ":web_push_encryption",
"key" => ":user_agent", "key" => ":vapid_details",
"value" => [":default"], "value" => [":nope"],
"db" => [":user_agent"] "db" => [":vapid_details"]
} }
], ],
"need_reboot" => false "need_reboot" => false
} }
assert Application.get_env(:http, :adapter) == [:someval] assert Application.get_env(:mime, :library) == [:someval]
assert Application.get_env(:http, :send_user_agent) == true assert Application.get_env(:prometheus, Pleroma.Web.Endpoint.MetricsExporter) == true
assert Application.get_env(:http, :user_agent) == [:default] assert Application.get_env(:web_push_encryption, :vapid_details) == [:nope]
end end
test "saving config with partial update", %{conn: conn} do test "saving config with partial update", %{conn: conn} do
@ -631,17 +622,17 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|> put_req_header("content-type", "application/json") |> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{ |> post("/api/pleroma/admin/config", %{
configs: [ configs: [
%{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"} %{group: ":pleroma", key: ":key1", value: "Pleroma.Web.Endpoint"}
] ]
}) })
assert json_response_and_validate_schema(conn, 200) == %{ assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [ "configs" => [
%{ %{
"group" => ":tesla", "group" => ":pleroma",
"key" => ":adapter", "key" => ":key1",
"value" => "Tesla.Adapter.Httpc", "value" => "Pleroma.Web.Endpoint",
"db" => [":adapter"] "db" => [":key1"]
} }
], ],
"need_reboot" => false "need_reboot" => false
@ -653,14 +644,14 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
admin: admin, admin: admin,
token: token token: token
} do } do
ueberauth = Application.get_env(:ueberauth, Ueberauth) prometheus = Application.get_env(:prometheus, Pleroma.Web.Endpoint.MetricsExporter)
insert(:config, key: :keyaa1) insert(:config, key: :keyaa1)
insert(:config, key: :keyaa2) insert(:config, key: :keyaa2)
config3 = config3 =
insert(:config, insert(:config,
group: :ueberauth, group: :prometheus,
key: Ueberauth key: Pleroma.Web.Endpoint.MetricsExporter
) )
conn = conn =
@ -693,7 +684,9 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert Application.get_env(:pleroma, :keyaa1) == "another_value" assert Application.get_env(:pleroma, :keyaa1) == "another_value"
assert Application.get_env(:pleroma, :keyaa2) == "another_value" assert Application.get_env(:pleroma, :keyaa2) == "another_value"
assert Application.get_env(:ueberauth, Ueberauth) == config3.value
assert Application.get_env(:prometheus, Pleroma.Web.Endpoint.MetricsExporter) ==
config3.value
conn = conn =
build_conn() build_conn()
@ -704,8 +697,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
configs: [ configs: [
%{group: ":pleroma", key: ":keyaa2", delete: true}, %{group: ":pleroma", key: ":keyaa2", delete: true},
%{ %{
group: ":ueberauth", group: ":prometheus",
key: "Ueberauth", key: "Pleroma.Web.Endpoint.MetricsExporter",
delete: true delete: true
} }
] ]
@ -716,7 +709,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
"need_reboot" => false "need_reboot" => false
} }
assert Application.get_env(:ueberauth, Ueberauth) == ueberauth assert Application.get_env(:prometheus, Pleroma.Web.Endpoint.MetricsExporter) == prometheus
refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2) refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
end end
@ -1021,8 +1014,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|> post("/api/pleroma/admin/config", %{ |> post("/api/pleroma/admin/config", %{
configs: [ configs: [
%{ %{
"group" => ":oban", "group" => ":pleroma",
"key" => ":queues", "key" => ":nonexist_oban_queues",
"value" => [ "value" => [
%{"tuple" => [":federator_incoming", 50]}, %{"tuple" => [":federator_incoming", 50]},
%{"tuple" => [":federator_outgoing", 50]}, %{"tuple" => [":federator_outgoing", 50]},
@ -1039,8 +1032,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert json_response_and_validate_schema(conn, 200) == %{ assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [ "configs" => [
%{ %{
"group" => ":oban", "group" => ":pleroma",
"key" => ":queues", "key" => ":nonexist_oban_queues",
"value" => [ "value" => [
%{"tuple" => [":federator_incoming", 50]}, %{"tuple" => [":federator_incoming", 50]},
%{"tuple" => [":federator_outgoing", 50]}, %{"tuple" => [":federator_outgoing", 50]},
@ -1245,6 +1238,57 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_whitelist) refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_whitelist)
end end
test "doesn't set keys in the blacklist", %{conn: conn} do
clear_config(:database_config_blacklist, [
{:pleroma, :logger},
{:pleroma, :key2},
{:pleroma, Pleroma.Captcha.NotReal},
{:blacklisted_group}
])
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: ":pleroma", key: ":logger", value: "logconf"},
%{group: ":pleroma", key: ":key1", value: "value1"},
%{group: ":pleroma", key: ":key2", value: "value2"},
%{group: ":not_real", key: ":anything", value: "value3"},
%{group: ":blacklisted_group", key: ":anything", value: "value4"}
]
})
assert Application.get_env(:pleroma, :logger) == nil
assert Application.get_env(:pleroma, :key1) == "value1"
assert Application.get_env(:pleroma, :key2) == nil
assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == nil
assert Application.get_env(:blacklisted_group, :anything) == nil
end
test "doesn't allow updating the database_config_blacklist itself", %{conn: conn} do
original_blacklist = Pleroma.Config.get(:database_config_blacklist)
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_blacklist)
%{"configs" => configs} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":pleroma",
key: ":database_config_blacklist",
value: [%{"tuple" => [":pleroma", ":key1"]}]
}
]
})
|> json_response_and_validate_schema(200)
assert configs == []
assert Pleroma.Config.get(:database_config_blacklist) == original_blacklist
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_blacklist)
end
test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
assert conn assert conn
|> put_req_header("content-type", "application/json") |> put_req_header("content-type", "application/json")
@ -1515,5 +1559,35 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert length(response) == length(Pleroma.Docs.JSON.compiled_descriptions()) assert length(response) == length(Pleroma.Docs.JSON.compiled_descriptions())
end end
test "filters by database configuration blacklist and whitelist", %{conn: conn} do
clear_config(:database_config_whitelist, [
{:pleroma, :instance},
{:pleroma, :activitypub},
{:pleroma, Pleroma.Upload}
])
clear_config(:database_config_blacklist, [
{:pleroma, Pleroma.Upload}
])
children =
conn
|> get("/api/pleroma/admin/config/descriptions")
|> json_response_and_validate_schema(200)
assert length(children) == 2
assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 2
instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
assert instance["children"]
activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
assert activitypub["children"]
web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
refute web_endpoint["children"]
end
end end
end end