Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into pleroma-double_mentions

This commit is contained in:
Lain Soykaf 2023-06-11 16:31:20 +04:00
commit 55dd8ef1c7
59 changed files with 400 additions and 238 deletions

View File

@ -18,6 +18,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Removed
- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
## 2.5.2
### Security
- `/proxy` endpoint now sets a Content-Security-Policy (sandbox)
- WebSocket endpoint now respects unauthenticated restrictions for streams of public posts
- OEmbed HTML tags are now filtered
### Changed
- docs: Be more explicit about the level of compatibility of OTP releases
- Set default background worker timeout to 15 minutes
### Fixed
- Atom/RSS formatting (HTML truncation, published, missing summary)
- Remove `static_fe` pipeline for `/users/:nickname/feed`
- Stop oban from retrying if validating errors occur when processing incoming data
- Make sure object refetching as used by already received polls follows MRF rules
### Removed
- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
## 2.5.1
### Added

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

@ -0,0 +1 @@
MediaProxy responses now return a sandbox CSP header

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

1
changelog.d/3880.remove Normal file
View File

@ -0,0 +1 @@
Cleanup OStatus-era user upgrades and ap_enabled indicator

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

@ -0,0 +1 @@
Allow non-HTTP(s) URIs in "url" fields for compatibility with "FEP-fffd: Proxy Objects"

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

@ -0,0 +1 @@
Fix opengraph and twitter card meta tags

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

@ -0,0 +1 @@
OEmbed HTML tags are now filtered

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

1
changelog.d/3896.add Normal file
View File

@ -0,0 +1 @@
Validate Host header for Uploads and return a 302 if the base_url has changed

1
changelog.d/3897.add Normal file
View File

@ -0,0 +1 @@
OnlyMedia Upload Filter

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

View File

@ -0,0 +1 @@
Preload: Make generated JSON html-safe. It already was html safe because it only consists of config data that is base64 encoded, but this will keep it safe it that ever changes.

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

View File

View File

@ -671,6 +671,12 @@ This filter reads the ImageDescription and iptc:Caption-Abstract fields with Exi
No specific configuration.
#### Pleroma.Upload.Filter.OnlyMedia
This filter rejects uploads that are not identified with Content-Type matching audio/\*, image/\*, or video/\*
No specific configuration.
#### Pleroma.Upload.Filter.Mogrify
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.

View File

@ -4,7 +4,7 @@
## Installation
This guide will assume you are on Debian 11 (“bullseye”) or later. This guide should also work with Ubuntu 18.04 (“Bionic Beaver”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
This guide will assume you are on Debian 12 (“bookworm”) or later. This guide should also work with Ubuntu 22.04 (“jammy”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
{! backend/installation/generic_dependencies.include !}

View File

@ -5,7 +5,7 @@
## インストール
このガイドはDebian Stretchを利用することを想定しています。Ubuntu 16.04や18.04でもおそらく動作します。また、ユーザはrootもしくはsudoにより管理者権限を持っていることを前提とします。もし、以下の操作をrootユーザで行う場合は、 `sudo` を無視してください。ただし、`sudo -Hu pleroma` のようにユーザを指定している場合には `su <username> -s $SHELL -c 'command'` を代わりに使ってください。
このガイドはDebian Bookwormを利用することを想定しています。Ubuntu 22.04でもおそらく動作します。また、ユーザはrootもしくはsudoにより管理者権限を持っていることを前提とします。もし、以下の操作をrootユーザで行う場合は、 `sudo` を無視してください。ただし、`sudo -Hu pleroma` のようにユーザを指定している場合には `su <username> -s $SHELL -c 'command'` を代わりに使ってください。
### 必要なソフトウェア

View File

@ -0,0 +1,23 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri do
use Ecto.Type
def type, do: :string
def cast(uri) when is_binary(uri) do
case URI.parse(uri) do
%URI{scheme: nil} -> :error
%URI{} -> {:ok, uri}
_ -> :error
end
end
def cast(_), do: :error
def dump(data), do: {:ok, data}
def load(data), do: {:ok, data}
end

View File

@ -38,9 +38,9 @@ defmodule Pleroma.Upload.Filter do
{:ok, :noop} ->
filter(rest, upload)
error ->
Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(error)}")
error
{:error, e} ->
Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(e)}")
{:error, e}
end
end
end

View File

@ -0,0 +1,20 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.OnlyMedia do
@behaviour Pleroma.Upload.Filter
alias Pleroma.Upload
def filter(%Upload{content_type: content_type}) do
[type, _subtype] = String.split(content_type, "/")
if type in ["image", "video", "audio"] do
{:ok, :noop}
else
{:error, "Disallowed content-type: #{content_type}"}
end
end
def filter(_), do: {:ok, :noop}
end

View File

@ -124,7 +124,6 @@ defmodule Pleroma.User do
field(:domain_blocks, {:array, :string}, default: [])
field(:is_active, :boolean, default: true)
field(:no_rich_text, :boolean, default: false)
field(:ap_enabled, :boolean, default: false)
field(:is_moderator, :boolean, default: false)
field(:is_admin, :boolean, default: false)
field(:show_role, :boolean, default: true)
@ -488,7 +487,6 @@ defmodule Pleroma.User do
:nickname,
:public_key,
:avatar,
:ap_enabled,
:banner,
:is_locked,
:last_refreshed_at,
@ -1061,11 +1059,7 @@ defmodule Pleroma.User do
end
def maybe_direct_follow(%User{} = follower, %User{} = followed) do
if not ap_enabled?(followed) do
follow(follower, followed)
else
{:ok, follower, followed}
end
{:ok, follower, followed}
end
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
@ -1898,7 +1892,6 @@ defmodule Pleroma.User do
confirmation_token: nil,
domain_blocks: [],
is_active: false,
ap_enabled: false,
is_moderator: false,
is_admin: false,
mascot: nil,
@ -2151,10 +2144,6 @@ defmodule Pleroma.User do
end
end
def ap_enabled?(%User{local: true}), do: true
def ap_enabled?(%User{ap_enabled: ap_enabled}), do: ap_enabled
def ap_enabled?(_), do: false
@doc "Gets or fetch a user by uri or nickname."
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)

View File

@ -1547,7 +1547,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
%{
ap_id: data["id"],
uri: get_actor_url(data["url"]),
ap_enabled: true,
banner: normalize_image(data["image"]),
fields: fields,
emoji: emojis,
@ -1668,7 +1667,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
def fetch_and_prepare_user_from_ap_id(ap_id, additional \\ []) do
defp fetch_and_prepare_user_from_ap_id(ap_id, additional) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
{:ok, data} <- user_data_from_user_object(data, additional) do
{:ok, maybe_update_follow_information(data)}
@ -1751,24 +1750,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def make_user_from_ap_id(ap_id, additional \\ []) do
user = User.get_cached_by_ap_id(ap_id)
if user && !User.ap_enabled?(user) do
Transmogrifier.upgrade_user_from_ap_id(ap_id)
else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
if user do
user
|> User.remote_user_changeset(data)
|> User.update_and_set_cache()
else
maybe_handle_clashing_nickname(data)
if user do
user
|> User.remote_user_changeset(data)
|> User.update_and_set_cache()
else
maybe_handle_clashing_nickname(data)
data
|> User.remote_user_changeset()
|> Repo.insert()
|> User.set_cache()
end
data
|> User.remote_user_changeset()
|> Repo.insert()
|> User.set_cache()
end
end
end

View File

@ -73,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do
end
defp maybe_refetch_user(%User{ap_id: ap_id}) do
Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(ap_id)
# Maybe it could use User.get_or_fetch_by_ap_id to avoid refreshing too often
User.fetch_by_ap_id(ap_id)
end
end

View File

@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
field(:like_count, :integer, default: 0)
field(:announcement_count, :integer, default: 0)
field(:inReplyTo, ObjectValidators.ObjectID)
field(:url, ObjectValidators.Uri)
field(:url, ObjectValidators.BareUri)
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])

View File

@ -199,7 +199,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
inboxes =
recipients
|> Enum.filter(&User.ap_enabled?/1)
|> Enum.map(fn actor -> actor.inbox end)
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|> Instances.filter_reachable()
@ -241,7 +240,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
json = Jason.encode!(data)
recipients(actor, activity)
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|> Enum.map(fn %User{} = user ->
determine_inbox(activity, user)
end)

View File

@ -20,7 +20,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
alias Pleroma.Workers.TransmogrifierWorker
import Ecto.Query
@ -946,47 +945,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
defp strip_internal_tags(object), do: object
def perform(:user_upgrade, user) do
# we pass a fake user so that the followers collection is stripped away
old_follower_address = User.ap_followers(%User{nickname: user.nickname})
from(
a in Activity,
where: ^old_follower_address in a.recipients,
update: [
set: [
recipients:
fragment(
"array_replace(?,?,?)",
a.recipients,
^old_follower_address,
^user.follower_address
)
]
]
)
|> Repo.update_all([])
end
def upgrade_user_from_ap_id(ap_id) do
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
{:ok, user} <- update_user(user, data) do
{:ok, _pid} = Task.start(fn -> ActivityPub.pinned_fetch_task(user) end)
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
{:ok, user}
else
%User{} = user -> {:ok, user}
e -> e
end
end
defp update_user(user, data) do
user
|> User.remote_user_changeset(data)
|> User.update_and_set_cache()
end
def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
Map.put(data, "url", url["href"])
end

View File

@ -6,7 +6,6 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Activity
alias Pleroma.Object.Containment
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Federator.Publisher
@ -80,7 +79,7 @@ defmodule Pleroma.Web.Federator do
# NOTE: we use the actor ID to do the containment, this is fine because an
# actor shouldn't be acting on objects outside their own AP server.
with {_, {:ok, _user}} <- {:actor, ap_enabled_actor(actor)},
with {_, {:ok, _user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
nil <- Activity.normalize(params["id"]),
{_, :ok} <-
{:correct_origin?, Containment.contain_origin_from_id(actor, params)},
@ -110,14 +109,4 @@ defmodule Pleroma.Web.Federator do
{:error, e}
end
end
def ap_enabled_actor(id) do
user = User.get_cached_by_ap_id(id)
if User.ap_enabled?(user) do
{:ok, user}
else
ActivityPub.make_user_from_ap_id(id)
end
end
end

View File

@ -12,6 +12,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
alias Pleroma.Web.MediaProxy
alias Plug.Conn
plug(:sandbox)
def remote(conn, %{"sig" => sig64, "url" => url64}) do
with {_, true} <- {:enabled, MediaProxy.enabled?()},
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
@ -202,4 +204,9 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
defp media_proxy_opts do
Config.get([:media_proxy, :proxy_opts], [])
end
defp sandbox(conn, _params) do
conn
|> merge_resp_headers([{"content-security-policy", "sandbox;"}])
end
end

View File

@ -76,9 +76,10 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
{:meta, [name: "twitter:card", content: "summary_large_image"], []},
{:meta,
[
name: "twitter:player",
name: "twitter:image",
content: MediaProxy.url(url["href"])
], []}
], []},
{:meta, [name: "twitter:image:alt", content: truncate(attachment["name"])], []}
| acc
]
|> maybe_add_dimensions(url)
@ -130,4 +131,12 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
metadata
end
end
defp truncate(nil), do: ""
defp truncate(text) do
# truncate to 420 characters
# see https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
Pleroma.Formatter.truncate(text, 420)
end
end

View File

@ -46,12 +46,32 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
config = Pleroma.Config.get(Pleroma.Upload)
with uploader <- Keyword.fetch!(config, :uploader),
%{scheme: media_scheme, host: media_host, port: media_port} =
Pleroma.Upload.base_url() |> URI.parse()
with {:valid_host, true} <- {:valid_host, match?(^media_host, conn.host)},
uploader <- Keyword.fetch!(config, :uploader),
proxy_remote = Keyword.get(config, :proxy_remote, false),
{:ok, get_method} <- uploader.get_file(file),
false <- media_is_banned(conn, get_method) do
get_media(conn, get_method, proxy_remote, opts)
else
{:valid_host, false} ->
redirect_url =
%URI{
scheme: media_scheme,
host: media_host,
port: media_port,
path: conn.request_path,
query: conn.query_string
}
|> URI.to_string()
|> String.trim_trailing("?")
conn
|> Phoenix.Controller.redirect(external: redirect_url)
|> halt()
_ ->
conn
|> send_resp(:internal_server_error, dgettext("errors", "Failed"))

View File

@ -11,7 +11,7 @@ defmodule Pleroma.Web.Preload do
terms =
params
|> parser.generate_terms()
|> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v))} end)
|> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v, escape: :html_safe))} end)
|> Enum.into(%{})
Map.merge(acc, terms)
@ -19,7 +19,7 @@ defmodule Pleroma.Web.Preload do
rendered_html =
preload_data
|> Jason.encode!()
|> Jason.encode!(escape: :html_safe)
|> build_script_tag()
|> HTML.safe_to_string()

View File

@ -6,8 +6,8 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html),
oembed_url when is_binary(oembed_url) <- get_oembed_url(elements),
{:ok, oembed_data} <- get_oembed_data(oembed_url) do
oembed_data
{:ok, oembed_data = %{"html" => html}} <- get_oembed_data(oembed_url) do
%{oembed_data | "html" => Pleroma.HTML.filter_tags(html)}
else
_e -> %{}
end

View File

@ -25,7 +25,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
true <- Visibility.is_public?(activity.object),
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
url = Helpers.url(conn) <> conn.request_path
meta =
Metadata.build_tags(%{
activity_id: notice_id,
object: activity.object,
user: user,
url: url
})
timeline =
activity.object.data["context"]

View File

@ -25,6 +25,7 @@ defmodule Pleroma.Web.Streamer do
def registry, do: @registry
@public_streams ["public", "public:local", "public:media", "public:local:media"]
@local_streams ["public:local", "public:local:media"]
@user_streams ["user", "user:notification", "direct", "user:pleroma_chat"]
@doc "Expands and authorizes a stream, and registers the process for streaming."
@ -41,14 +42,37 @@ defmodule Pleroma.Web.Streamer do
end
end
defp can_access_stream(user, oauth_token, kind) do
with {_, true} <- {:restrict?, Config.restrict_unauthenticated_access?(:timelines, kind)},
{_, %User{id: user_id}, %Token{user_id: user_id}} <- {:user, user, oauth_token},
{_, true} <-
{:scopes,
OAuthScopesPlug.filter_descendants(["read:statuses"], oauth_token.scopes) != []} do
true
else
{:restrict?, _} ->
true
_ ->
false
end
end
@doc "Expand and authorizes a stream"
@spec get_topic(stream :: String.t(), User.t() | nil, Token.t() | nil, Map.t()) ::
{:ok, topic :: String.t()} | {:error, :bad_topic}
def get_topic(stream, user, oauth_token, params \\ %{})
# Allow all public steams.
def get_topic(stream, _user, _oauth_token, _params) when stream in @public_streams do
{:ok, stream}
# Allow all public steams if the instance allows unauthenticated access.
# Otherwise, only allow users with valid oauth tokens.
def get_topic(stream, user, oauth_token, _params) when stream in @public_streams do
kind = if stream in @local_streams, do: :local, else: :federated
if can_access_stream(user, oauth_token, kind) do
{:ok, stream}
else
{:error, :unauthorized}
end
end
# Allow all hashtags streams.
@ -57,12 +81,20 @@ defmodule Pleroma.Web.Streamer do
end
# Allow remote instance streams.
def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
{:ok, "public:remote:" <> instance}
def get_topic("public:remote", user, oauth_token, %{"instance" => instance} = _params) do
if can_access_stream(user, oauth_token, :federated) do
{:ok, "public:remote:" <> instance}
else
{:error, :unauthorized}
end
end
def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
{:ok, "public:remote:media:" <> instance}
def get_topic("public:remote:media", user, oauth_token, %{"instance" => instance} = _params) do
if can_access_stream(user, oauth_token, :federated) do
{:ok, "public:remote:media:" <> instance}
else
{:error, :unauthorized}
end
end
# Expand user streams.

View File

@ -1,18 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.TransmogrifierWorker do
alias Pleroma.User
use Pleroma.Workers.WorkerHelper, queue: "transmogrifier"
@impl Oban.Worker
def perform(%Job{args: %{"op" => "user_upgrade", "user_id" => user_id}}) do
user = User.get_cached_by_id(user_id)
Pleroma.Web.ActivityPub.Transmogrifier.perform(:user_upgrade, user)
end
@impl Oban.Worker
def timeout(_job), do: :timer.seconds(5)
end

View File

@ -4,10 +4,10 @@ defmodule Pleroma.Mixfile do
def project do
[
app: :pleroma,
version: version("2.5.51"),
version: version("2.5.52"),
elixir: "~> 1.11",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
compilers: [:phoenix] ++ Mix.compilers(),
elixirc_options: [warnings_as_errors: warnings_as_errors()],
xref: [exclude: [:eldap]],
start_permanent: Mix.env() == :prod,
@ -127,10 +127,7 @@ defmodule Pleroma.Mixfile do
{:plug_cowboy, "~> 2.3"},
# oban 2.14 requires Elixir 1.12+
{:oban, "~> 2.13.4"},
{:gettext,
git: "https://github.com/tusooa/gettext.git",
ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808",
override: true},
{:gettext, "~> 0.20"},
{:bcrypt_elixir, "~> 2.2"},
{:trailing_format_plug, "~> 0.0.7"},
{:fast_sanitize, "~> 0.2.0"},

View File

@ -42,6 +42,7 @@
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
"expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"},
"fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"},
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
@ -49,7 +50,7 @@
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
"floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"},
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
"gettext": {:git, "https://github.com/tusooa/gettext.git", "72fb2496b6c5280ed911bdc3756890e7f38a4808", [ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808"]},
"gettext": {:hex, :gettext, "0.22.2", "6bfca374de34ecc913a28ba391ca184d88d77810a3e427afa8454a71a51341ac", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "8a2d389673aea82d7eae387e6a2ccc12660610080ae7beb19452cfdc1ec30f60"},
"gun": {:hex, :gun, "2.0.0", "2326bc0fd6d9cf628419708270d6fe8b02b8d002cf992e4165a77d997b1defd0", [:make, :rebar3], [{:cowlib, "2.12.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6613cb7c62930dc8d58263c44dda72f8556346ba88358fc929dcbc5f76d04569"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},

View File

@ -9,7 +9,6 @@
msgid ""
msgstr ""
"Language: en_test\n"
"Plural-Forms: nplurals=2\n"
#, elixir-format
#: lib/pleroma/web/api_spec/render_error.ex:122

View File

@ -9,7 +9,6 @@
msgid ""
msgstr ""
"Language: en_test\n"
"Plural-Forms: nplurals=2\n"
msgid "can't be blank"
msgstr ""

View File

@ -9,7 +9,6 @@
msgid ""
msgstr ""
"Language: en_test\n"
"Plural-Forms: nplurals=2\n"
msgid "eperm"
msgstr ""

View File

@ -21,10 +21,6 @@ msgstr ""
#~ ##
#~ ## Use "mix gettext.extract --merge" or "mix gettext.merge"
#~ ## to merge POT files into PO files.
#~ msgid ""
#~ msgstr ""
#~ "Language: en_test\n"
#~ "Plural-Forms: nplurals=2\n"
#, elixir-format
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9

View File

@ -9,7 +9,6 @@
msgid ""
msgstr ""
"Language: ru\n"
"Plural-Forms: nplurals=3\n"
msgid "can't be blank"
msgstr "не может быть пустым"

View File

@ -24,10 +24,6 @@ msgstr ""
##
## Use "mix gettext.extract --merge" or "mix gettext.merge"
## to merge POT files into PO files.
#~ msgid ""
#~ msgstr ""
#~ "Language: zh_Hans\n"
#~ "Plural-Forms: nplurals=1\n"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9
#, elixir-format

View File

@ -0,0 +1,13 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Repo.Migrations.RemoveUserApEnabled do
use Ecto.Migration
def change do
alter table(:users) do
remove(:ap_enabled, :boolean, default: false, null: false)
end
end
end

View File

@ -0,0 +1,25 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUriTest do
use Pleroma.DataCase, async: true
alias Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri
test "diaspora://" do
text = "diaspora://alice@fediverse.example/post/deadbeefdeadbeefdeadbeefdeadbeef"
assert {:ok, text} = BareUri.cast(text)
end
test "nostr:" do
text = "nostr:note1gwdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
assert {:ok, text} = BareUri.cast(text)
end
test "errors for non-URIs" do
assert :error == SafeText.cast(1)
assert :error == SafeText.cast("foo")
assert :error == SafeText.cast("foo bar")
end
end

View File

@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.OnlyMediaTest do
use Pleroma.DataCase, async: true
alias Pleroma.Upload
alias Pleroma.Upload.Filter.OnlyMedia
test "Allows media Content-Type" do
["audio/mpeg", "image/jpeg", "video/mp4"]
|> Enum.each(fn type ->
upload = %Upload{
content_type: type
}
assert {:ok, :noop} = OnlyMedia.filter(upload)
end)
end
test "Disallows non-media Content-Type" do
["application/javascript", "application/pdf", "text/html"]
|> Enum.each(fn type ->
upload = %Upload{
content_type: type
}
assert {:error, _} = OnlyMedia.filter(upload)
end)
end
end

View File

@ -1844,7 +1844,6 @@ defmodule Pleroma.UserTest do
confirmation_token: "qqqq",
domain_blocks: ["lain.com"],
is_active: false,
ap_enabled: true,
is_moderator: true,
is_admin: true,
mascot: %{"a" => "b"},
@ -1885,7 +1884,6 @@ defmodule Pleroma.UserTest do
confirmation_token: nil,
domain_blocks: [],
is_active: false,
ap_enabled: false,
is_moderator: false,
is_admin: false,
mascot: nil,
@ -2473,8 +2471,7 @@ defmodule Pleroma.UserTest do
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
ap_enabled: true
following_address: "http://localhost:4001/users/masto_closed/following"
)
assert other_user.following_count == 0
@ -2495,8 +2492,7 @@ defmodule Pleroma.UserTest do
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
ap_enabled: true
following_address: "http://localhost:4001/users/masto_closed/following"
)
assert other_user.following_count == 0
@ -2517,8 +2513,7 @@ defmodule Pleroma.UserTest do
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
ap_enabled: true
following_address: "http://localhost:4001/users/masto_closed/following"
)
assert other_user.following_count == 0

View File

@ -575,7 +575,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
user =
insert(:user,
ap_id: "https://mastodon.example.org/users/raymoo",
ap_enabled: true,
local: false,
last_refreshed_at: nil
)

View File

@ -174,7 +174,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.ap_id == user_id
assert user.nickname == "admin@mastodon.example.org"
assert user.ap_enabled
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
end

View File

@ -276,8 +276,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
follower =
insert(:user, %{
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
})
actor = insert(:user, follower_address: follower.ap_id)
@ -313,8 +312,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
follower =
insert(:user, %{
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
})
actor = insert(:user, follower_address: follower.ap_id)
@ -348,8 +346,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
follower =
insert(:user, %{
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
})
actor = insert(:user, follower_address: follower.ap_id)
@ -382,15 +379,13 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
fetcher =
insert(:user,
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
)
another_fetcher =
insert(:user,
local: false,
inbox: "https://domain2.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain2.com/users/nick1/inbox"
)
actor = insert(:user)

View File

@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
@ -353,69 +352,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
end
end
describe "user upgrade" do
test "it upgrades a user to activitypub" do
user =
insert(:user, %{
nickname: "rye@niu.moe",
local: false,
ap_id: "https://niu.moe/users/rye",
follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
})
user_two = insert(:user)
Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)
{:ok, activity} = CommonAPI.post(user, %{status: "test"})
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"})
assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
user = User.get_cached_by_id(user.id)
assert user.note_count == 1
{:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
ObanHelpers.perform_all()
assert user.ap_enabled
assert user.note_count == 1
assert user.follower_address == "https://niu.moe/users/rye/followers"
assert user.following_address == "https://niu.moe/users/rye/following"
user = User.get_cached_by_id(user.id)
assert user.note_count == 1
activity = Activity.get_by_id(activity.id)
assert user.follower_address in activity.recipients
assert %{
"url" => [
%{
"href" =>
"https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
}
]
} = user.avatar
assert %{
"url" => [
%{
"href" =>
"https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
}
]
} = user.banner
refute "..." in activity.recipients
unrelated_activity = Activity.get_by_id(unrelated_activity.id)
refute user.follower_address in unrelated_activity.recipients
user_two = User.get_cached_by_id(user_two.id)
assert User.following?(user_two, user)
refute "..." in User.following(user_two)
end
end
describe "actor rewriting" do
test "it fixes the actor URL property to be a proper URI" do
data = %{

View File

@ -1339,7 +1339,7 @@ defmodule Pleroma.Web.CommonAPITest do
test "cancels a pending follow for a remote user" do
follower = insert(:user)
followed = insert(:user, is_locked: true, local: false, ap_enabled: true)
followed = insert(:user, is_locked: true, local: false)
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
CommonAPI.follow(follower, followed)

View File

@ -78,16 +78,14 @@ defmodule Pleroma.Web.FederatorTest do
local: false,
nickname: "nick1@domain.com",
ap_id: "https://domain.com/users/nick1",
inbox: inbox1,
ap_enabled: true
inbox: inbox1
})
insert(:user, %{
local: false,
nickname: "nick2@domain2.com",
ap_id: "https://domain2.com/users/nick2",
inbox: inbox2,
ap_enabled: true
inbox: inbox2
})
dt = NaiveDateTime.utc_now()

View File

@ -6,7 +6,9 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
use Pleroma.Web.ConnCase
import Mock
import Mox
alias Pleroma.ReverseProxy.ClientMock
alias Pleroma.Web.MediaProxy
alias Plug.Conn
@ -74,6 +76,20 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
end
end
test "it applies sandbox CSP to MediaProxy requests", %{conn: conn} do
media_url = "https://lain.com/image.png"
media_proxy_url = MediaProxy.encode_url(media_url)
ClientMock
|> expect(:request, fn :get, ^media_url, _, _, _ ->
{:ok, 200, [{"content-type", "image/png"}]}
end)
%Conn{resp_headers: headers} = get(conn, media_proxy_url)
assert {"content-security-policy", "sandbox;"} in headers
end
end
describe "Media Preview Proxy" do

View File

@ -182,7 +182,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
{:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []},
{:meta, [name: "twitter:description", content: "pleroma in a nutshell"], []},
{:meta, [name: "twitter:card", content: "summary_large_image"], []},
{:meta, [name: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []},
{:meta, [name: "twitter:image", content: "https://pleroma.gov/tenshi.png"], []},
{:meta, [name: "twitter:image:alt", content: ""], []},
{:meta, [name: "twitter:player:width", content: "1280"], []},
{:meta, [name: "twitter:player:height", content: "1024"], []},
{:meta, [name: "twitter:card", content: "player"], []},

View File

@ -40,4 +40,30 @@ defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do
&(&1 == {"content-disposition", ~s[inline; filename="\\"cofe\\".gif"]})
)
end
test "denies access to media if wrong Host", %{
attachment_url: attachment_url
} do
conn = get(build_conn(), attachment_url)
assert conn.status == 200
new_media_base = "http://media.localhost:8080"
%{scheme: new_media_scheme, host: new_media_host, port: new_media_port} =
URI.parse(new_media_base)
clear_config([Pleroma.Upload, :base_url], new_media_base)
conn = get(build_conn(), attachment_url)
expected_url =
URI.parse(attachment_url)
|> Map.put(:host, new_media_host)
|> Map.put(:port, new_media_port)
|> Map.put(:scheme, new_media_scheme)
|> URI.to_string()
assert redirected_to(conn, 302) == expected_url
end
end

View File

@ -129,7 +129,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
}}
end
test "parses OEmbed" do
test "parses OEmbed and filters HTML tags" do
assert Parser.parse("http://example.com/oembed") ==
{:ok,
%{
@ -139,7 +139,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
"flickr_type" => "photo",
"height" => "768",
"html" =>
"<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by \u202E\u202D\u202Cbees\u202C, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
"<a href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by \u202E\u202D\u202Cbees\u202C, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"/></a>",
"license" => "All Rights Reserved",
"license_id" => 0,
"provider_name" => "Flickr",

View File

@ -29,6 +29,26 @@ defmodule Pleroma.Web.StreamerTest do
assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)
end
test "rejects local public streams if restricted_unauthenticated is on" do
clear_config([:restrict_unauthenticated, :timelines, :local], true)
assert {:error, :unauthorized} = Streamer.get_topic("public:local", nil, nil)
assert {:error, :unauthorized} = Streamer.get_topic("public:local:media", nil, nil)
end
test "rejects remote public streams if restricted_unauthenticated is on" do
clear_config([:restrict_unauthenticated, :timelines, :federated], true)
assert {:error, :unauthorized} = Streamer.get_topic("public", nil, nil)
assert {:error, :unauthorized} = Streamer.get_topic("public:media", nil, nil)
assert {:error, :unauthorized} =
Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"})
assert {:error, :unauthorized} =
Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"})
end
test "allows instance streams" do
assert {:ok, "public:remote:lain.com"} =
Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"})
@ -69,6 +89,63 @@ defmodule Pleroma.Web.StreamerTest do
end
end
test "allows local public streams if restricted_unauthenticated is on", %{
user: user,
token: oauth_token
} do
clear_config([:restrict_unauthenticated, :timelines, :local], true)
%{token: read_notifications_token} = oauth_access(["read:notifications"], user: user)
%{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user)
assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token)
assert {:ok, "public:local:media"} =
Streamer.get_topic("public:local:media", user, oauth_token)
for token <- [read_notifications_token, badly_scoped_token] do
assert {:error, :unauthorized} = Streamer.get_topic("public:local", user, token)
assert {:error, :unauthorized} = Streamer.get_topic("public:local:media", user, token)
end
end
test "allows remote public streams if restricted_unauthenticated is on", %{
user: user,
token: oauth_token
} do
clear_config([:restrict_unauthenticated, :timelines, :federated], true)
%{token: read_notifications_token} = oauth_access(["read:notifications"], user: user)
%{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user)
assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token)
assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token)
assert {:ok, "public:remote:lain.com"} =
Streamer.get_topic("public:remote", user, oauth_token, %{"instance" => "lain.com"})
assert {:ok, "public:remote:media:lain.com"} =
Streamer.get_topic("public:remote:media", user, oauth_token, %{
"instance" => "lain.com"
})
for token <- [read_notifications_token, badly_scoped_token] do
assert {:error, :unauthorized} = Streamer.get_topic("public", user, token)
assert {:error, :unauthorized} = Streamer.get_topic("public:media", user, token)
assert {:error, :unauthorized} =
Streamer.get_topic("public:remote", user, token, %{
"instance" => "lain.com"
})
assert {:error, :unauthorized} =
Streamer.get_topic("public:remote:media", user, token, %{
"instance" => "lain.com"
})
end
end
test "allows user streams (with proper OAuth token scopes)", %{
user: user,
token: read_oauth_token

View File

@ -50,7 +50,6 @@ defmodule Pleroma.Factory do
last_refreshed_at: NaiveDateTime.utc_now(),
notification_settings: %Pleroma.User.NotificationSetting{},
multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
ap_enabled: true,
keys: pem
}