diff options
Diffstat (limited to 'lib')
27 files changed, 495 insertions, 203 deletions
| 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/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/ensure_authenticated_plug.ex b/lib/pleroma/plugs/ensure_authenticated_plug.ex index 6f9b840a9..054d2297f 100644 --- a/lib/pleroma/plugs/ensure_authenticated_plug.ex +++ b/lib/pleroma/plugs/ensure_authenticated_plug.ex @@ -15,9 +15,24 @@ defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do      conn    end -  def call(conn, _) do +  def call(conn, options) do +    perform = +      cond do +        options[:if_func] -> options[:if_func].() +        options[:unless_func] -> !options[:unless_func].() +        true -> true +      end + +    if perform do +      fail(conn) +    else +      conn +    end +  end + +  def fail(conn) do      conn      |> render_error(:forbidden, "Invalid credentials.") -    |> halt +    |> halt()    end  end diff --git a/lib/pleroma/plugs/federating_plug.ex b/lib/pleroma/plugs/federating_plug.ex index d3943586d..7d947339f 100644 --- a/lib/pleroma/plugs/federating_plug.ex +++ b/lib/pleroma/plugs/federating_plug.ex @@ -10,14 +10,20 @@ defmodule Pleroma.Web.FederatingPlug do    end    def call(conn, _opts) do -    if Pleroma.Config.get([:instance, :federating]) do +    if federating?() do        conn      else -      conn -      |> put_status(404) -      |> Phoenix.Controller.put_view(Pleroma.Web.ErrorView) -      |> Phoenix.Controller.render("404.json") -      |> halt() +      fail(conn)      end    end + +  def federating?, do: Pleroma.Config.get([:instance, :federating]) + +  defp fail(conn) do +    conn +    |> put_status(404) +    |> Phoenix.Controller.put_view(Pleroma.Web.ErrorView) +    |> Phoenix.Controller.render("404.json") +    |> halt() +  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 35b973b56..4bbeb493c 100644 --- a/lib/pleroma/reverse_proxy/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy/reverse_proxy.ex @@ -5,7 +5,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) @@ -32,9 +32,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 @@ -291,16 +288,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 7531757f5..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 @@ -839,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} @@ -850,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}) @@ -1320,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()} @@ -1458,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 @@ -2062,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/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 779de0e4d..8b9eb4a2c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    alias Pleroma.Delivery    alias Pleroma.Object    alias Pleroma.Object.Fetcher +  alias Pleroma.Plugs.EnsureAuthenticatedPlug    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.InternalFetchActor @@ -18,23 +19,37 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    alias Pleroma.Web.ActivityPub.UserView    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.ActivityPub.Visibility +  alias Pleroma.Web.FederatingPlug    alias Pleroma.Web.Federator    require Logger    action_fallback(:errors) +  @federating_only_actions [:internal_fetch, :relay, :relay_following, :relay_followers] + +  plug(FederatingPlug when action in @federating_only_actions) + +  plug( +    EnsureAuthenticatedPlug, +    [unless_func: &FederatingPlug.federating?/0] when action not in @federating_only_actions +  ) + +  plug( +    EnsureAuthenticatedPlug +    when action in [:read_inbox, :update_outbox, :whoami, :upload_media, :following, :followers] +  ) +    plug(      Pleroma.Plugs.Cache,      [query_params: false, tracking_fun: &__MODULE__.track_object_fetch/2]      when action in [:activity, :object]    ) -  plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay])    plug(:set_requester_reachable when action in [:inbox])    plug(:relay_active? when action in [:relay]) -  def relay_active?(conn, _) do +  defp relay_active?(conn, _) do      if Pleroma.Config.get([:instance, :allow_relay]) do        conn      else @@ -127,11 +142,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    end    # GET /relay/following -  def following(%{assigns: %{relay: true}} = conn, _params) do -    conn -    |> put_resp_content_type("application/activity+json") -    |> put_view(UserView) -    |> render("following.json", %{user: Relay.get_actor()}) +  def relay_following(conn, _params) do +    with %{halted: false} = conn <- FederatingPlug.call(conn, []) do +      conn +      |> put_resp_content_type("application/activity+json") +      |> put_view(UserView) +      |> render("following.json", %{user: Relay.get_actor()}) +    end    end    def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do @@ -164,11 +181,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    end    # GET /relay/followers -  def followers(%{assigns: %{relay: true}} = conn, _params) do -    conn -    |> put_resp_content_type("application/activity+json") -    |> put_view(UserView) -    |> render("followers.json", %{user: Relay.get_actor()}) +  def relay_followers(conn, _params) do +    with %{halted: false} = conn <- FederatingPlug.call(conn, []) do +      conn +      |> put_resp_content_type("application/activity+json") +      |> put_view(UserView) +      |> render("followers.json", %{user: Relay.get_actor()}) +    end    end    def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do @@ -200,13 +219,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      end    end -  def outbox(conn, %{"nickname" => nickname, "page" => page?} = params) +  def outbox( +        %{assigns: %{user: for_user}} = conn, +        %{"nickname" => nickname, "page" => page?} = params +      )        when page? in [true, "true"] do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {:ok, user} <- User.ensure_keys_present(user) do        activities =          if params["max_id"] do -          ActivityPub.fetch_user_activities(user, nil, %{ +          ActivityPub.fetch_user_activities(user, for_user, %{              "max_id" => params["max_id"],              # This is a hack because postgres generates inefficient queries when filtering by              # 'Answer', poll votes will be hidden by the visibility filter in this case anyway @@ -214,7 +236,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do              "limit" => 10            })          else -          ActivityPub.fetch_user_activities(user, nil, %{ +          ActivityPub.fetch_user_activities(user, for_user, %{              "limit" => 10,              "include_poll_votes" => true            }) @@ -255,8 +277,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      json(conn, "ok")    end -  # only accept relayed Creates -  def inbox(conn, %{"type" => "Create"} = params) do +  # POST /relay/inbox -or- POST /internal/fetch/inbox +  def inbox(conn, params) do +    if params["type"] == "Create" && FederatingPlug.federating?() do +      post_inbox_relayed_create(conn, params) +    else +      post_inbox_fallback(conn, params) +    end +  end + +  defp post_inbox_relayed_create(conn, params) do      Logger.debug(        "Signature missing or not from author, relayed Create message, fetching object from source"      ) @@ -266,10 +296,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      json(conn, "ok")    end -  def inbox(conn, params) do +  defp post_inbox_fallback(conn, params) do      headers = Enum.into(conn.req_headers, %{}) -    if String.contains?(headers["signature"], params["actor"]) do +    if headers["signature"] && params["actor"] && +         String.contains?(headers["signature"], params["actor"]) do        Logger.debug(          "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"        ) @@ -277,7 +308,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do        Logger.debug(inspect(conn.req_headers))      end -    json(conn, dgettext("errors", "error")) +    conn +    |> put_status(:bad_request) +    |> json(dgettext("errors", "error"))    end    defp represent_service_actor(%User{} = user, conn) do @@ -311,10 +344,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      |> render("user.json", %{user: user})    end -  def whoami(_conn, _params), do: {:error, :not_found} -    def read_inbox( -        %{assigns: %{user: %{nickname: nickname} = user}} = conn, +        %{assigns: %{user: %User{nickname: nickname} = user}} = conn,          %{"nickname" => nickname, "page" => page?} = params        )        when page? in [true, "true"] do @@ -337,7 +368,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      })    end -  def read_inbox(%{assigns: %{user: %{nickname: nickname} = user}} = conn, %{ +  def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{          "nickname" => nickname        }) do      with {:ok, user} <- User.ensure_keys_present(user) do @@ -348,15 +379,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      end    end -  def read_inbox(%{assigns: %{user: nil}} = conn, %{"nickname" => nickname}) do -    err = dgettext("errors", "can't read inbox of %{nickname}", nickname: nickname) - -    conn -    |> put_status(:forbidden) -    |> json(err) -  end - -  def read_inbox(%{assigns: %{user: %{nickname: as_nickname}}} = conn, %{ +  def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{          "nickname" => nickname        }) do      err = @@ -370,7 +393,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      |> json(err)    end -  def handle_user_activity(user, %{"type" => "Create"} = params) do +  defp handle_user_activity(%User{} = user, %{"type" => "Create"} = params) do      object =        params["object"]        |> Map.merge(Map.take(params, ["to", "cc"])) @@ -386,7 +409,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      })    end -  def handle_user_activity(user, %{"type" => "Delete"} = params) do +  defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do      with %Object{} = object <- Object.normalize(params["object"]),           true <- user.is_moderator || user.ap_id == object.data["actor"],           {:ok, delete} <- ActivityPub.delete(object) do @@ -396,7 +419,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      end    end -  def handle_user_activity(user, %{"type" => "Like"} = params) do +  defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do      with %Object{} = object <- Object.normalize(params["object"]),           {:ok, activity, _object} <- ActivityPub.like(user, object) do        {:ok, activity} @@ -405,7 +428,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      end    end -  def handle_user_activity(_, _) do +  defp handle_user_activity(_, _) do      {:error, dgettext("errors", "Unhandled activity type")}    end @@ -434,7 +457,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      end    end -  def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = _) do +  def update_outbox(%{assigns: %{user: %User{} = user}} = conn, %{"nickname" => nickname}) do      err =        dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",          nickname: nickname, @@ -446,13 +469,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      |> json(err)    end -  def errors(conn, {:error, :not_found}) do +  defp errors(conn, {:error, :not_found}) do      conn      |> put_status(:not_found)      |> json(dgettext("errors", "Not found"))    end -  def errors(conn, _e) do +  defp errors(conn, _e) do      conn      |> put_status(:internal_server_error)      |> json(dgettext("errors", "error")) @@ -492,7 +515,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    - HTTP Code: 201 Created    - HTTP Body: ActivityPub object to be inserted into another's `attachment` field    """ -  def upload_media(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do +  def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do      with {:ok, object} <-             ActivityPub.upload(               file, 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 47b7d2da3..175260bc2 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -745,14 +745,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      end    end -  def list_statuses(%{assigns: %{user: admin}} = conn, params) do +  def list_statuses(%{assigns: %{user: _admin}} = conn, params) do      godmode = params["godmode"] == "true" || params["godmode"] == true      local_only = params["local_only"] == "true" || params["local_only"] == true      with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true      {page, page_size} = page_params(params)      activities = -      ActivityPub.fetch_statuses(admin, %{ +      ActivityPub.fetch_statuses(nil, %{          "godmode" => godmode,          "local_only" => local_only,          "limit" => page_size, 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 348fdedf1..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 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/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index 59aabb549..9ba602d9f 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -25,7 +25,12 @@ defmodule Pleroma.Web.Feed.UserController do    def feed_redirect(%{assigns: %{format: format}} = conn, _params)        when format in ["json", "activity+json"] do -    ActivityPubController.call(conn, :user) +    with %{halted: false} = conn <- +           Pleroma.Plugs.EnsureAuthenticatedPlug.call(conn, +             unless_func: &Pleroma.Web.FederatingPlug.federating?/0 +           ) do +      ActivityPubController.call(conn, :user) +    end    end    def feed_redirect(conn, %{"nickname" => nickname}) do 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/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index c443c888c..6fd3cfce5 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -16,6 +16,10 @@ defmodule Pleroma.Web.OStatus.OStatusController do    alias Pleroma.Web.Metadata.PlayerView    alias Pleroma.Web.Router +  plug(Pleroma.Plugs.EnsureAuthenticatedPlug, +    unless_func: &Pleroma.Web.FederatingPlug.federating?/0 +  ) +    plug(      RateLimiter,      [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity] @@ -135,13 +139,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do      end    end -  def errors(conn, {:error, :not_found}) do +  defp errors(conn, {:error, :not_found}) do      render_error(conn, :not_found, "Not found")    end -  def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) +  defp errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) -  def errors(conn, _) do +  defp errors(conn, _) do      render_error(conn, :internal_server_error, "Something went wrong")    end  end 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/router.ex b/lib/pleroma/web/router.ex index 980242c68..e4e3ee704 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -541,6 +541,7 @@ defmodule Pleroma.Web.Router do      get("/mailer/unsubscribe/:token", Mailer.SubscriptionController, :unsubscribe)    end +  # Server to Server (S2S) AP interactions    pipeline :activitypub do      plug(:accepts, ["activity+json", "json"])      plug(Pleroma.Web.Plugs.HTTPSignaturePlug) @@ -554,6 +555,7 @@ defmodule Pleroma.Web.Router do      get("/users/:nickname/outbox", ActivityPubController, :outbox)    end +  # Client to Server (C2S) AP interactions    pipeline :activitypub_client do      plug(:accepts, ["activity+json", "json"])      plug(:fetch_session) @@ -597,8 +599,8 @@ defmodule Pleroma.Web.Router do        post("/inbox", ActivityPubController, :inbox)      end -    get("/following", ActivityPubController, :following, assigns: %{relay: true}) -    get("/followers", ActivityPubController, :followers, assigns: %{relay: true}) +    get("/following", ActivityPubController, :relay_following) +    get("/followers", ActivityPubController, :relay_followers)    end    scope "/internal/fetch", Pleroma.Web.ActivityPub do diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index 5ac75f1c4..7f9464268 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -17,6 +17,10 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do    plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)    plug(:assign_id) +  plug(Pleroma.Plugs.EnsureAuthenticatedPlug, +    unless_func: &Pleroma.Web.FederatingPlug.federating?/0 +  ) +    @page_keys ["max_id", "min_id", "limit", "since_id", "order"]    defp get_title(%Object{data: %{"name" => name}}) when is_binary(name), @@ -33,7 +37,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do      |> render("error.html", %{message: message, meta: ""})    end -  def get_counts(%Activity{} = activity) do +  defp get_counts(%Activity{} = activity) do      %Object{data: data} = Object.normalize(activity)      %{ @@ -43,9 +47,9 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do      }    end -  def represent(%Activity{} = activity), do: represent(activity, false) +  defp represent(%Activity{} = activity), do: represent(activity, false) -  def represent(%Activity{object: %Object{data: data}} = activity, selected) do +  defp represent(%Activity{object: %Object{data: data}} = activity, selected) do      {:ok, user} = User.get_or_fetch(activity.object.data["actor"])      link = @@ -54,10 +58,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 +120,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, @@ -147,17 +158,17 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do      end    end -  def assign_id(%{path_info: ["notice", notice_id]} = conn, _opts), +  defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),      do: assign(conn, :notice_id, notice_id) -  def assign_id(%{path_info: ["users", user_id]} = conn, _opts), +  defp assign_id(%{path_info: ["users", user_id]} = conn, _opts),      do: assign(conn, :username_or_id, user_id) -  def assign_id(%{path_info: ["objects", object_id]} = conn, _opts), +  defp assign_id(%{path_info: ["objects", object_id]} = conn, _opts),      do: assign(conn, :object_id, object_id) -  def assign_id(%{path_info: ["activities", activity_id]} = conn, _opts), +  defp assign_id(%{path_info: ["activities", activity_id]} = conn, _opts),      do: assign(conn, :activity_id, activity_id) -  def assign_id(conn, _opts), do: conn +  defp assign_id(conn, _opts), do: conn  end diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex index fbf31c7eb..89da760da 100644 --- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex @@ -16,6 +16,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do    @status_types ["Article", "Event", "Note", "Video", "Page", "Question"] +  plug(Pleroma.Web.FederatingPlug) +    # Note: follower can submit the form (with password auth) not being signed in (having no token)    plug(      OAuthScopesPlug, diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index bca0e26eb..537f9f778 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -17,6 +17,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.WebFinger +  plug(Pleroma.Web.FederatingPlug when action == :remote_subscribe) +    plug(      OAuthScopesPlug,      %{scopes: ["follow", "write:follows"]} 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) | 
