aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma')
-rw-r--r--lib/pleroma/activity.ex7
-rw-r--r--lib/pleroma/activity/ir/topics.ex2
-rw-r--r--lib/pleroma/application.ex1
-rw-r--r--lib/pleroma/config/holder.ex33
-rw-r--r--lib/pleroma/config/loader.ex30
-rw-r--r--lib/pleroma/config/transfer_task.ex2
-rw-r--r--lib/pleroma/docs/json.ex2
-rw-r--r--lib/pleroma/earmark_renderer.ex256
-rw-r--r--lib/pleroma/plugs/rate_limiter/rate_limiter.ex27
-rw-r--r--lib/pleroma/plugs/remote_ip.ex7
-rw-r--r--lib/pleroma/plugs/static_fe_plug.ex5
-rw-r--r--lib/pleroma/plugs/uploaded_media.ex7
-rw-r--r--lib/pleroma/reverse_proxy/reverse_proxy.ex20
-rw-r--r--lib/pleroma/user.ex65
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex19
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex39
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex7
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/account_view.ex4
-rw-r--r--lib/pleroma/web/common_api/utils.ex4
-rw-r--r--lib/pleroma/web/endpoint.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex14
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/auth_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex19
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex33
-rw-r--r--lib/pleroma/web/static_fe/static_fe_controller.ex13
-rw-r--r--lib/pleroma/workers/background_worker.ex4
27 files changed, 454 insertions, 172 deletions
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 397eb6e3f..6ca05f74e 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -308,6 +308,13 @@ defmodule Pleroma.Activity do
|> where([a], fragment("? ->> 'state' = 'pending'", a.data))
end
+ def following_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
+ Queries.by_type("Follow")
+ |> where([a], fragment("?->>'state' = 'pending'", a.data))
+ |> where([a], a.actor == ^ap_id)
+ |> Repo.all()
+ end
+
def restrict_deactivated_users(query) do
deactivated_users =
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex
index 4acc1a3e0..9e65bedad 100644
--- a/lib/pleroma/activity/ir/topics.ex
+++ b/lib/pleroma/activity/ir/topics.ex
@@ -39,7 +39,7 @@ defmodule Pleroma.Activity.Ir.Topics do
end
end
- defp item_creation_tags(tags, %{data: %{"type" => "Create"}} = object, activity) do
+ defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 18854b850..33f1705df 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -31,6 +31,7 @@ defmodule Pleroma.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
+ Pleroma.Config.Holder.save_default()
Pleroma.HTML.compile_scrubbers()
Pleroma.Config.DeprecationWarnings.warn()
Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
diff --git a/lib/pleroma/config/holder.ex b/lib/pleroma/config/holder.ex
index f1a339703..f037d5d48 100644
--- a/lib/pleroma/config/holder.ex
+++ b/lib/pleroma/config/holder.ex
@@ -3,14 +3,33 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Holder do
- @config Pleroma.Config.Loader.load_and_merge()
+ @config Pleroma.Config.Loader.default_config()
- @spec config() :: keyword()
- def config, do: @config
+ @spec save_default() :: :ok
+ def save_default do
+ default_config =
+ if System.get_env("RELEASE_NAME") do
+ release_config =
+ [:code.root_dir(), "releases", System.get_env("RELEASE_VSN"), "releases.exs"]
+ |> Path.join()
+ |> Pleroma.Config.Loader.read()
- @spec config(atom()) :: any()
- def config(group), do: @config[group]
+ Pleroma.Config.Loader.merge(@config, release_config)
+ else
+ @config
+ end
- @spec config(atom(), atom()) :: any()
- def config(group, key), do: @config[group][key]
+ Pleroma.Config.put(:default_config, default_config)
+ end
+
+ @spec default_config() :: keyword()
+ def default_config, do: get_default()
+
+ @spec default_config(atom()) :: keyword()
+ def default_config(group), do: Keyword.get(get_default(), group)
+
+ @spec default_config(atom(), atom()) :: keyword()
+ def default_config(group, key), do: get_in(get_default(), [group, key])
+
+ defp get_default, do: Pleroma.Config.get(:default_config)
end
diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex
index df2d18725..6ca6550bd 100644
--- a/lib/pleroma/config/loader.ex
+++ b/lib/pleroma/config/loader.ex
@@ -13,32 +13,28 @@ defmodule Pleroma.Config.Loader do
]
if Code.ensure_loaded?(Config.Reader) do
- @spec load(Path.t()) :: keyword()
- def load(path), do: Config.Reader.read!(path)
+ @reader Config.Reader
- defp do_merge(conf1, conf2), do: Config.Reader.merge(conf1, conf2)
+ def read(path), do: @reader.read!(path)
else
# support for Elixir less than 1.9
- @spec load(Path.t()) :: keyword()
- def load(path) do
+ @reader Mix.Config
+ def read(path) do
path
- |> Mix.Config.eval!()
+ |> @reader.eval!()
|> elem(0)
end
-
- defp do_merge(conf1, conf2), do: Mix.Config.merge(conf1, conf2)
end
- @spec load_and_merge() :: keyword()
- def load_and_merge do
- all_paths =
- if Pleroma.Config.get(:release),
- do: ["config/config.exs", "config/releases.exs"],
- else: ["config/config.exs"]
+ @spec read(Path.t()) :: keyword()
+
+ @spec merge(keyword(), keyword()) :: keyword()
+ def merge(c1, c2), do: @reader.merge(c1, c2)
- all_paths
- |> Enum.map(&load(&1))
- |> Enum.reduce([], &do_merge(&2, &1))
+ @spec default_config() :: keyword()
+ def default_config do
+ "config/config.exs"
+ |> read()
|> filter()
end
diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex
index 435fc7450..7c3449b5e 100644
--- a/lib/pleroma/config/transfer_task.ex
+++ b/lib/pleroma/config/transfer_task.ex
@@ -83,7 +83,7 @@ defmodule Pleroma.Config.TransferTask do
key = ConfigDB.from_string(setting.key)
group = ConfigDB.from_string(setting.group)
- default = Pleroma.Config.Holder.config(group, key)
+ default = Pleroma.Config.Holder.default_config(group, key)
value = ConfigDB.from_binary(setting.value)
merged_value =
diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex
index 6508a7bdb..74f8b2615 100644
--- a/lib/pleroma/docs/json.ex
+++ b/lib/pleroma/docs/json.ex
@@ -15,7 +15,7 @@ defmodule Pleroma.Docs.JSON do
end
def compile do
- with config <- Pleroma.Config.Loader.load("config/description.exs") do
+ with config <- Pleroma.Config.Loader.read("config/description.exs") do
config[:pleroma][:config_description]
|> Pleroma.Docs.Generator.convert_to_strings()
|> Jason.encode!()
diff --git a/lib/pleroma/earmark_renderer.ex b/lib/pleroma/earmark_renderer.ex
new file mode 100644
index 000000000..6211a3b4a
--- /dev/null
+++ b/lib/pleroma/earmark_renderer.ex
@@ -0,0 +1,256 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+#
+# This file is derived from Earmark, under the following copyright:
+# Copyright © 2014 Dave Thomas, The Pragmatic Programmers
+# SPDX-License-Identifier: Apache-2.0
+# Upstream: https://github.com/pragdave/earmark/blob/master/lib/earmark/html_renderer.ex
+defmodule Pleroma.EarmarkRenderer do
+ @moduledoc false
+
+ alias Earmark.Block
+ alias Earmark.Context
+ alias Earmark.HtmlRenderer
+ alias Earmark.Options
+
+ import Earmark.Inline, only: [convert: 3]
+ import Earmark.Helpers.HtmlHelpers
+ import Earmark.Message, only: [add_messages_from: 2, get_messages: 1, set_messages: 2]
+ import Earmark.Context, only: [append: 2, set_value: 2]
+ import Earmark.Options, only: [get_mapper: 1]
+
+ @doc false
+ def render(blocks, %Context{options: %Options{}} = context) do
+ messages = get_messages(context)
+
+ {contexts, html} =
+ get_mapper(context.options).(
+ blocks,
+ &render_block(&1, put_in(context.options.messages, []))
+ )
+ |> Enum.unzip()
+
+ all_messages =
+ contexts
+ |> Enum.reduce(messages, fn ctx, messages1 -> messages1 ++ get_messages(ctx) end)
+
+ {put_in(context.options.messages, all_messages), html |> IO.iodata_to_binary()}
+ end
+
+ #############
+ # Paragraph #
+ #############
+ defp render_block(%Block.Para{lnb: lnb, lines: lines, attrs: attrs}, context) do
+ lines = convert(lines, lnb, context)
+ add_attrs(lines, "<p>#{lines.value}</p>", attrs, [], lnb)
+ end
+
+ ########
+ # Html #
+ ########
+ defp render_block(%Block.Html{html: html}, context) do
+ {context, html}
+ end
+
+ defp render_block(%Block.HtmlComment{lines: lines}, context) do
+ {context, lines}
+ end
+
+ defp render_block(%Block.HtmlOneline{html: html}, context) do
+ {context, html}
+ end
+
+ #########
+ # Ruler #
+ #########
+ defp render_block(%Block.Ruler{lnb: lnb, attrs: attrs}, context) do
+ add_attrs(context, "<hr />", attrs, [], lnb)
+ end
+
+ ###########
+ # Heading #
+ ###########
+ defp render_block(
+ %Block.Heading{lnb: lnb, level: level, content: content, attrs: attrs},
+ context
+ ) do
+ converted = convert(content, lnb, context)
+ html = "<h#{level}>#{converted.value}</h#{level}>"
+ add_attrs(converted, html, attrs, [], lnb)
+ end
+
+ ##############
+ # Blockquote #
+ ##############
+
+ defp render_block(%Block.BlockQuote{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
+ {context1, body} = render(blocks, context)
+ html = "<blockquote>#{body}</blockquote>"
+ add_attrs(context1, html, attrs, [], lnb)
+ end
+
+ #########
+ # Table #
+ #########
+
+ defp render_block(
+ %Block.Table{lnb: lnb, header: header, rows: rows, alignments: aligns, attrs: attrs},
+ context
+ ) do
+ {context1, html} = add_attrs(context, "<table>", attrs, [], lnb)
+ context2 = set_value(context1, html)
+
+ context3 =
+ if header do
+ append(add_trs(append(context2, "<thead>"), [header], "th", aligns, lnb), "</thead>")
+ else
+ # Maybe an error, needed append(context, html)
+ context2
+ end
+
+ context4 = append(add_trs(append(context3, "<tbody>"), rows, "td", aligns, lnb), "</tbody>")
+
+ {context4, [context4.value, "</table>"]}
+ end
+
+ ########
+ # Code #
+ ########
+
+ defp render_block(
+ %Block.Code{lnb: lnb, language: language, attrs: attrs} = block,
+ %Context{options: options} = context
+ ) do
+ class =
+ if language, do: ~s{ class="#{code_classes(language, options.code_class_prefix)}"}, else: ""
+
+ tag = ~s[<pre><code#{class}>]
+ lines = options.render_code.(block)
+ html = ~s[#{tag}#{lines}</code></pre>]
+ add_attrs(context, html, attrs, [], lnb)
+ end
+
+ #########
+ # Lists #
+ #########
+
+ defp render_block(
+ %Block.List{lnb: lnb, type: type, blocks: items, attrs: attrs, start: start},
+ context
+ ) do
+ {context1, content} = render(items, context)
+ html = "<#{type}#{start}>#{content}</#{type}>"
+ add_attrs(context1, html, attrs, [], lnb)
+ end
+
+ # format a single paragraph list item, and remove the para tags
+ defp render_block(
+ %Block.ListItem{lnb: lnb, blocks: blocks, spaced: false, attrs: attrs},
+ context
+ )
+ when length(blocks) == 1 do
+ {context1, content} = render(blocks, context)
+ content = Regex.replace(~r{</?p>}, content, "")
+ html = "<li>#{content}</li>"
+ add_attrs(context1, html, attrs, [], lnb)
+ end
+
+ # format a spaced list item
+ defp render_block(%Block.ListItem{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
+ {context1, content} = render(blocks, context)
+ html = "<li>#{content}</li>"
+ add_attrs(context1, html, attrs, [], lnb)
+ end
+
+ ##################
+ # Footnote Block #
+ ##################
+
+ defp render_block(%Block.FnList{blocks: footnotes}, context) do
+ items =
+ Enum.map(footnotes, fn note ->
+ blocks = append_footnote_link(note)
+ %Block.ListItem{attrs: "#fn:#{note.number}", type: :ol, blocks: blocks}
+ end)
+
+ {context1, html} = render_block(%Block.List{type: :ol, blocks: items}, context)
+ {context1, Enum.join([~s[<div class="footnotes">], "<hr />", html, "</div>"])}
+ end
+
+ #######################################
+ # Isolated IALs are rendered as paras #
+ #######################################
+
+ defp render_block(%Block.Ial{verbatim: verbatim}, context) do
+ {context, "<p>{:#{verbatim}}</p>"}
+ end
+
+ ####################
+ # IDDef is ignored #
+ ####################
+
+ defp render_block(%Block.IdDef{}, context), do: {context, ""}
+
+ #####################################
+ # And here are the inline renderers #
+ #####################################
+
+ defdelegate br, to: HtmlRenderer
+ defdelegate codespan(text), to: HtmlRenderer
+ defdelegate em(text), to: HtmlRenderer
+ defdelegate strong(text), to: HtmlRenderer
+ defdelegate strikethrough(text), to: HtmlRenderer
+
+ defdelegate link(url, text), to: HtmlRenderer
+ defdelegate link(url, text, title), to: HtmlRenderer
+
+ defdelegate image(path, alt, title), to: HtmlRenderer
+
+ defdelegate footnote_link(ref, backref, number), to: HtmlRenderer
+
+ # Table rows
+ defp add_trs(context, rows, tag, aligns, lnb) do
+ numbered_rows =
+ rows
+ |> Enum.zip(Stream.iterate(lnb, &(&1 + 1)))
+
+ numbered_rows
+ |> Enum.reduce(context, fn {row, lnb}, ctx ->
+ append(add_tds(append(ctx, "<tr>"), row, tag, aligns, lnb), "</tr>")
+ end)
+ end
+
+ defp add_tds(context, row, tag, aligns, lnb) do
+ Enum.reduce(1..length(row), context, add_td_fn(row, tag, aligns, lnb))
+ end
+
+ defp add_td_fn(row, tag, aligns, lnb) do
+ fn n, ctx ->
+ style =
+ case Enum.at(aligns, n - 1, :default) do
+ :default -> ""
+ align -> " style=\"text-align: #{align}\""
+ end
+
+ col = Enum.at(row, n - 1)
+ converted = convert(col, lnb, set_messages(ctx, []))
+ append(add_messages_from(ctx, converted), "<#{tag}#{style}>#{converted.value}</#{tag}>")
+ end
+ end
+
+ ###############################
+ # Append Footnote Return Link #
+ ###############################
+
+ defdelegate append_footnote_link(note), to: HtmlRenderer
+ defdelegate append_footnote_link(note, fnlink), to: HtmlRenderer
+
+ defdelegate render_code(lines), to: HtmlRenderer
+
+ defp code_classes(language, prefix) do
+ ["" | String.split(prefix || "")]
+ |> Enum.map(fn pfx -> "#{pfx}#{language}" end)
+ |> Enum.join(" ")
+ end
+end
diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex
index c3f6351c8..1529da717 100644
--- a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex
+++ b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex
@@ -78,7 +78,7 @@ defmodule Pleroma.Plugs.RateLimiter do
end
def call(conn, plug_opts) do
- if disabled?() do
+ if disabled?(conn) do
handle_disabled(conn)
else
action_settings = action_settings(plug_opts)
@@ -87,9 +87,9 @@ defmodule Pleroma.Plugs.RateLimiter do
end
defp handle_disabled(conn) do
- if Config.get(:env) == :prod do
- Logger.warn("Rate limiter is disabled for localhost/socket")
- end
+ Logger.warn(
+ "Rate limiter disabled due to forwarded IP not being found. Please ensure your reverse proxy is providing the X-Forwarded-For header or disable the RemoteIP plug/rate limiter."
+ )
conn
end
@@ -109,16 +109,21 @@ defmodule Pleroma.Plugs.RateLimiter do
end
end
- def disabled? do
+ def disabled?(conn) do
localhost_or_socket =
- Config.get([Pleroma.Web.Endpoint, :http, :ip])
- |> Tuple.to_list()
- |> Enum.join(".")
- |> String.match?(~r/^local|^127.0.0.1/)
+ case Config.get([Pleroma.Web.Endpoint, :http, :ip]) do
+ {127, 0, 0, 1} -> true
+ {0, 0, 0, 0, 0, 0, 0, 1} -> true
+ {:local, _} -> true
+ _ -> false
+ end
- remote_ip_disabled = not Config.get([Pleroma.Plugs.RemoteIp, :enabled])
+ remote_ip_not_found =
+ if Map.has_key?(conn.assigns, :remote_ip_found),
+ do: !conn.assigns.remote_ip_found,
+ else: false
- localhost_or_socket and remote_ip_disabled
+ localhost_or_socket and remote_ip_not_found
end
@inspect_bucket_not_found {:error, :not_found}
diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex
index 2eca4f8f6..0ac9050d0 100644
--- a/lib/pleroma/plugs/remote_ip.ex
+++ b/lib/pleroma/plugs/remote_ip.ex
@@ -7,6 +7,8 @@ defmodule Pleroma.Plugs.RemoteIp do
This is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
"""
+ import Plug.Conn
+
@behaviour Plug
@headers ~w[
@@ -26,11 +28,12 @@ defmodule Pleroma.Plugs.RemoteIp do
def init(_), do: nil
- def call(conn, _) do
+ def call(%{remote_ip: original_remote_ip} = conn, _) do
config = Pleroma.Config.get(__MODULE__, [])
if Keyword.get(config, :enabled, false) do
- RemoteIp.call(conn, remote_ip_opts(config))
+ %{remote_ip: new_remote_ip} = conn = RemoteIp.call(conn, remote_ip_opts(config))
+ assign(conn, :remote_ip_found, original_remote_ip != new_remote_ip)
else
conn
end
diff --git a/lib/pleroma/plugs/static_fe_plug.ex b/lib/pleroma/plugs/static_fe_plug.ex
index deebe4879..156e6788e 100644
--- a/lib/pleroma/plugs/static_fe_plug.ex
+++ b/lib/pleroma/plugs/static_fe_plug.ex
@@ -21,6 +21,9 @@ defmodule Pleroma.Plugs.StaticFEPlug do
defp enabled?, do: Pleroma.Config.get([:static_fe, :enabled], false)
defp accepts_html?(conn) do
- conn |> get_req_header("accept") |> List.first() |> String.contains?("text/html")
+ case get_req_header(conn, "accept") do
+ [accept | _] -> String.contains?(accept, "text/html")
+ _ -> false
+ end
end
end
diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex
index f372829a2..36ff024a7 100644
--- a/lib/pleroma/plugs/uploaded_media.ex
+++ b/lib/pleroma/plugs/uploaded_media.ex
@@ -14,9 +14,14 @@ defmodule Pleroma.Plugs.UploadedMedia do
# no slashes
@path "media"
+ @default_cache_control_header "public, max-age=1209600"
+
def init(_opts) do
static_plug_opts =
- []
+ [
+ headers: %{"cache-control" => @default_cache_control_header},
+ cache_control_for_etags: @default_cache_control_header
+ ]
|> Keyword.put(:from, "__unconfigured_media_plug")
|> Keyword.put(:at, "/__unconfigured_media_plug")
|> Plug.Static.init()
diff --git a/lib/pleroma/reverse_proxy/reverse_proxy.ex b/lib/pleroma/reverse_proxy/reverse_proxy.ex
index a281a00dc..8b713b8f4 100644
--- a/lib/pleroma/reverse_proxy/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy/reverse_proxy.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.ReverseProxy do
@keep_req_headers ~w(accept user-agent accept-encoding cache-control if-modified-since) ++
~w(if-unmodified-since if-none-match if-range range)
- @resp_cache_headers ~w(etag date last-modified cache-control)
+ @resp_cache_headers ~w(etag date last-modified)
@keep_resp_headers @resp_cache_headers ++
~w(content-type content-disposition content-encoding content-range) ++
~w(accept-ranges vary)
@@ -34,9 +34,6 @@ defmodule Pleroma.ReverseProxy do
* request: `#{inspect(@keep_req_headers)}`
* response: `#{inspect(@keep_resp_headers)}`
- If no caching headers (`#{inspect(@resp_cache_headers)}`) are returned by upstream, `cache-control` will be
- set to `#{inspect(@default_cache_control_header)}`.
-
Options:
* `redirect_on_failure` (default `false`). Redirects the client to the real remote URL if there's any HTTP
@@ -297,16 +294,17 @@ defmodule Pleroma.ReverseProxy do
defp build_resp_cache_headers(headers, _opts) do
has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)
- has_cache_control? = List.keymember?(headers, "cache-control", 0)
cond do
- has_cache? && has_cache_control? ->
- headers
-
has_cache? ->
- # There's caching header present but no cache-control -- we need to explicitely override it
- # to public as Plug defaults to "max-age=0, private, must-revalidate"
- List.keystore(headers, "cache-control", 0, {"cache-control", "public"})
+ # There's caching header present but no cache-control -- we need to set our own
+ # as Plug defaults to "max-age=0, private, must-revalidate"
+ List.keystore(
+ headers,
+ "cache-control",
+ 0,
+ {"cache-control", @default_cache_control_header}
+ )
true ->
List.keystore(
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 5fe79333e..911dde6e2 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -16,6 +16,7 @@ defmodule Pleroma.User do
alias Pleroma.Conversation.Participation
alias Pleroma.Delivery
alias Pleroma.FollowingRelationship
+ alias Pleroma.HTML
alias Pleroma.Keys
alias Pleroma.Notification
alias Pleroma.Object
@@ -530,7 +531,14 @@ defmodule Pleroma.User do
end
def maybe_validate_required_email(changeset, true), do: changeset
- def maybe_validate_required_email(changeset, _), do: validate_required(changeset, [:email])
+
+ def maybe_validate_required_email(changeset, _) do
+ if Pleroma.Config.get([:instance, :account_activation_required]) do
+ validate_required(changeset, [:email])
+ else
+ changeset
+ end
+ end
defp put_ap_id(changeset) do
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
@@ -832,10 +840,6 @@ defmodule Pleroma.User do
_e ->
with [_nick, _domain] <- String.split(nickname, "@"),
{:ok, user} <- fetch_by_nickname(nickname) do
- if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
- fetch_initial_posts(user)
- end
-
{:ok, user}
else
_e -> {:error, "not found " <> nickname}
@@ -843,11 +847,6 @@ defmodule Pleroma.User do
end
end
- @doc "Fetch some posts when the user has just been federated with"
- def fetch_initial_posts(user) do
- BackgroundWorker.enqueue("fetch_initial_posts", %{"user_id" => user.id})
- end
-
@spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
def get_followers_query(%User{} = user, nil) do
User.Query.build(%{followers: user, deactivated: false})
@@ -1313,16 +1312,6 @@ defmodule Pleroma.User do
Repo.delete(user)
end
- def perform(:fetch_initial_posts, %User{} = user) do
- pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
-
- # Insert all the posts in reverse order, so they're in the right order on the timeline
- user.source_data["outbox"]
- |> Utils.fetch_ordered_collection(pages)
- |> Enum.reverse()
- |> Enum.each(&Pleroma.Web.Federator.incoming_ap_doc/1)
- end
-
def perform(:deactivate_async, user, status), do: deactivate(user, status)
@spec perform(atom(), User.t(), list()) :: list() | {:error, any()}
@@ -1451,18 +1440,7 @@ defmodule Pleroma.User do
if !is_nil(user) and !needs_update?(user) do
{:ok, user}
else
- # Whether to fetch initial posts for the user (if it's a new user & the fetching is enabled)
- should_fetch_initial = is_nil(user) and Pleroma.Config.get([:fetch_initial_posts, :enabled])
-
- resp = fetch_by_ap_id(ap_id)
-
- if should_fetch_initial do
- with {:ok, %User{} = user} <- resp do
- fetch_initial_posts(user)
- end
- end
-
- resp
+ fetch_by_ap_id(ap_id)
end
end
@@ -2055,4 +2033,27 @@ defmodule Pleroma.User do
|> validate_required([:invisible])
|> update_and_set_cache()
end
+
+ def sanitize_html(%User{} = user) do
+ sanitize_html(user, nil)
+ end
+
+ # User data that mastodon isn't filtering (treated as plaintext):
+ # - field name
+ # - display name
+ def sanitize_html(%User{} = user, filter) do
+ fields =
+ user
+ |> User.fields()
+ |> Enum.map(fn %{"name" => name, "value" => value} ->
+ %{
+ "name" => name,
+ "value" => HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
+ }
+ end)
+
+ user
+ |> Map.put(:bio, HTML.filter_tags(user.bio, filter))
+ |> Map.put(:fields, fields)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index bb5542c89..729c23af7 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -60,15 +60,28 @@ defmodule Pleroma.Web.ActivityPub.Relay do
def publish(_), do: {:error, "Not implemented"}
- @spec list() :: {:ok, [String.t()]} | {:error, any()}
- def list do
+ @spec list(boolean()) :: {:ok, [String.t()]} | {:error, any()}
+ def list(with_not_accepted \\ false) do
with %User{} = user <- get_actor() do
- list =
+ accepted =
user
|> User.following()
|> Enum.map(fn entry -> URI.parse(entry).host end)
|> Enum.uniq()
+ list =
+ if with_not_accepted do
+ without_accept =
+ user
+ |> Pleroma.Activity.following_requests_for_actor()
+ |> Enum.map(fn a -> URI.parse(a.data["object"]).host <> " (no Accept received)" end)
+ |> Enum.uniq()
+
+ accepted ++ without_accept
+ else
+ accepted
+ end
+
{:ok, list}
else
error -> format_error(error)
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 2bc958670..15dd2ed45 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -784,45 +784,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
defp build_flag_object(_), do: []
- @doc """
- Fetches the OrderedCollection/OrderedCollectionPage from `from`, limiting the amount of pages fetched after
- the first one to `pages_left` pages.
- If the amount of pages is higher than the collection has, it returns whatever was there.
- """
- def fetch_ordered_collection(from, pages_left, acc \\ []) do
- with {:ok, response} <- Tesla.get(from),
- {:ok, collection} <- Jason.decode(response.body) do
- case collection["type"] do
- "OrderedCollection" ->
- # If we've encountered the OrderedCollection and not the page,
- # just call the same function on the page address
- fetch_ordered_collection(collection["first"], pages_left)
-
- "OrderedCollectionPage" ->
- if pages_left > 0 do
- # There are still more pages
- if Map.has_key?(collection, "next") do
- # There are still more pages, go deeper saving what we have into the accumulator
- fetch_ordered_collection(
- collection["next"],
- pages_left - 1,
- acc ++ collection["orderedItems"]
- )
- else
- # No more pages left, just return whatever we already have
- acc ++ collection["orderedItems"]
- end
- else
- # Got the amount of pages needed, add them all to the accumulator
- acc ++ collection["orderedItems"]
- end
-
- _ ->
- {:error, "Not an OrderedCollection or OrderedCollectionPage"}
- end
- end
- end
-
#### Report-related helpers
def get_reports(params, page, page_size) do
params =
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index c0358b678..bc21ac6c7 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -73,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
+ user = User.sanitize_html(user)
endpoints = render("endpoints.json", %{user: user})
@@ -81,12 +82,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
fields =
user
|> User.fields()
- |> Enum.map(fn %{"name" => name, "value" => value} ->
- %{
- "name" => Pleroma.HTML.strip_tags(name),
- "value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
- }
- end)
|> Enum.map(&Map.put(&1, "type", "PropertyValue"))
%{
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex
index de0755ee5..47b7d2da3 100644
--- a/lib/pleroma/web/admin_api/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/admin_api_controller.ex
@@ -834,7 +834,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
configs = ConfigDB.get_all_as_keyword()
merged =
- Config.Holder.config()
+ Config.Holder.default_config()
|> ConfigDB.merge(configs)
|> Enum.map(fn {group, values} ->
Enum.map(values, fn {key, value} ->
diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex
index 619390ef4..1e03849de 100644
--- a/lib/pleroma/web/admin_api/views/account_view.ex
+++ b/lib/pleroma/web/admin_api/views/account_view.ex
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.AdminAPI.AccountView do
use Pleroma.Web, :view
- alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.MediaProxy
@@ -26,7 +25,8 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
def render("show.json", %{user: user}) do
avatar = User.avatar_url(user) |> MediaProxy.url()
- display_name = HTML.strip_tags(user.name || user.nickname)
+ display_name = Pleroma.HTML.strip_tags(user.name || user.nickname)
+ user = User.sanitize_html(user, FastSanitize.Sanitizer.StripTags)
%{
"id" => user.id,
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 8746273c4..635e7cd38 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -331,7 +331,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def format_input(text, "text/markdown", options) do
text
|> Formatter.mentions_escape(options)
- |> Earmark.as_html!()
+ |> Earmark.as_html!(%Earmark.Options{renderer: Pleroma.EarmarkRenderer})
|> Formatter.linkify(options)
|> Formatter.html_escape("text/html")
end
@@ -591,7 +591,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
limit = Pleroma.Config.get([:instance, :limit])
length = String.length(full_payload)
- if length < limit do
+ if length <= limit do
:ok
else
{:error, dgettext("errors", "The status is over the character limit")}
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 118c3ac6f..72cb3ee27 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -12,7 +12,7 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Plugs.HTTPSecurityPlug)
plug(Pleroma.Plugs.UploadedMedia)
- @static_cache_control "public max-age=86400 must-revalidate"
+ @static_cache_control "public, no-cache"
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index dc3b47415..88c997b9f 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -76,7 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@doc "POST /api/v1/accounts"
def create(
%{assigns: %{app: app}} = conn,
- %{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params
+ %{"username" => nickname, "password" => _, "agreement" => true} = params
) do
params =
params
@@ -93,7 +93,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|> Map.put("bio", params["bio"] || "")
|> Map.put("confirm", params["password"])
- with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
+ with :ok <- validate_email_param(params),
+ {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do
json(conn, %{
token_type: "Bearer",
@@ -114,6 +115,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
render_error(conn, :forbidden, "Invalid credentials")
end
+ defp validate_email_param(%{"email" => _}), do: :ok
+
+ defp validate_email_param(_) do
+ case Pleroma.Config.get([:instance, :account_activation_required]) do
+ true -> {:error, %{"error" => "Missing parameters"}}
+ _ -> :ok
+ end
+ end
+
@doc "GET /api/v1/accounts/verify_credentials"
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
index f165c9965..37b389382 100644
--- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
@@ -86,6 +86,6 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
@spec get_or_make_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
defp get_or_make_app do
%{client_name: @local_mastodon_name, redirect_uris: "."}
- |> App.get_or_make(["read", "write", "follow", "push"])
+ |> App.get_or_make(["read", "write", "follow", "push", "admin"])
end
end
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 6dc191250..341dc2c91 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.MastodonAPI.AccountView do
use Pleroma.Web, :view
- alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
@@ -67,6 +66,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
defp do_render("show.json", %{user: user} = opts) do
+ user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
display_name = user.name || user.nickname
image = User.avatar_url(user) |> MediaProxy.url()
@@ -100,17 +100,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
}
end)
- fields =
- user
- |> User.fields()
- |> Enum.map(fn %{"name" => name, "value" => value} ->
- %{
- "name" => name,
- "value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
- }
- end)
-
- bio = HTML.filter_tags(user.bio, User.html_filter_policy(opts[:for]))
relationship = render("relationship.json", %{user: opts[:for], target: user})
%{
@@ -123,17 +112,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
followers_count: followers_count,
following_count: following_count,
statuses_count: user.note_count,
- note: bio || "",
+ note: user.bio || "",
url: User.profile_url(user),
avatar: image,
avatar_static: image,
header: header,
header_static: header,
emojis: emojis,
- fields: fields,
+ fields: user.fields,
bot: bot,
source: %{
- note: HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
+ note: Pleroma.HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
sensitive: false,
fields: user.raw_fields,
pleroma: %{
diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
index 0e160bbfc..dae7f0f2f 100644
--- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
@@ -101,6 +101,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
conn
|> put_view(ConversationView)
|> render("participation.json", %{participation: participation, for: user})
+ else
+ _error ->
+ conn
+ |> put_status(404)
+ |> json(%{"error" => "Unknown conversation id"})
end
end
@@ -108,9 +113,9 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
%{assigns: %{user: user}} = conn,
%{"id" => participation_id} = params
) do
- participation = Participation.get(participation_id, preload: [:conversation])
-
- if user.id == participation.user_id do
+ with %Participation{} = participation <-
+ Participation.get(participation_id, preload: [:conversation]),
+ true <- user.id == participation.user_id do
params =
params
|> Map.put("blocking_user", user)
@@ -126,6 +131,11 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|> add_link_headers(activities)
|> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity})
+ else
+ _error ->
+ conn
+ |> put_status(404)
+ |> json(%{"error" => "Unknown conversation id"})
end
end
@@ -133,15 +143,22 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
%{assigns: %{user: user}} = conn,
%{"id" => participation_id, "recipients" => recipients}
) do
- participation =
- participation_id
- |> Participation.get()
-
- with true <- user.id == participation.user_id,
+ with %Participation{} = participation <- Participation.get(participation_id),
+ true <- user.id == participation.user_id,
{:ok, participation} <- Participation.set_recipients(participation, recipients) do
conn
|> put_view(ConversationView)
|> render("participation.json", %{participation: participation, for: user})
+ else
+ {:error, message} ->
+ conn
+ |> put_status(:bad_request)
+ |> json(%{"error" => message})
+
+ _error ->
+ conn
+ |> put_status(404)
+ |> json(%{"error" => "Unknown conversation id"})
end
end
diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex
index 5ac75f1c4..98977bc19 100644
--- a/lib/pleroma/web/static_fe/static_fe_controller.ex
+++ b/lib/pleroma/web/static_fe/static_fe_controller.ex
@@ -54,10 +54,17 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
_ -> data["url"] || data["external_url"] || data["id"]
end
+ content =
+ if data["content"] do
+ Pleroma.HTML.filter_tags(data["content"])
+ else
+ nil
+ end
+
%{
- user: user,
+ user: User.sanitize_html(user),
title: get_title(activity.object),
- content: data["content"] || nil,
+ content: content,
attachment: data["attachment"],
link: link,
published: data["published"],
@@ -109,7 +116,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
next_page_id = List.last(timeline) && List.last(timeline).id
render(conn, "profile.html", %{
- user: user,
+ user: User.sanitize_html(user),
timeline: timeline,
prev_page_id: prev_page_id,
next_page_id: next_page_id,
diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex
index 598df6580..0f8ece2c4 100644
--- a/lib/pleroma/workers/background_worker.ex
+++ b/lib/pleroma/workers/background_worker.ex
@@ -10,10 +10,6 @@ defmodule Pleroma.Workers.BackgroundWorker do
use Pleroma.Workers.WorkerHelper, queue: "background"
@impl Oban.Worker
- def perform(%{"op" => "fetch_initial_posts", "user_id" => user_id}, _job) do
- user = User.get_cached_by_id(user_id)
- User.perform(:fetch_initial_posts, user)
- end
def perform(%{"op" => "deactivate_user", "user_id" => user_id, "status" => status}, _job) do
user = User.get_cached_by_id(user_id)