Merge remote-tracking branch 'remotes/origin/develop' into 1560-non-federating-instan...
authorIvan Tashkinov <ivantashkinov@gmail.com>
Fri, 6 Mar 2020 08:48:30 +0000 (11:48 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Fri, 6 Mar 2020 08:48:30 +0000 (11:48 +0300)
1  2 
lib/pleroma/plugs/federating_plug.ex
lib/pleroma/plugs/static_fe_plug.ex
lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/ostatus/ostatus_controller.ex
test/plugs/oauth_plug_test.exs
test/web/activity_pub/activity_pub_controller_test.exs
test/web/ostatus/ostatus_controller_test.exs

index 456c1bfb9d9fe472edc86855db15403885573521,d3943586d41226785a348164a8115081e3da4a71..c6d622ce461ad4fdb9dabf3fb7b42f6fd5ec7ff0
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  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])
 +
 +  def fail(conn) do
 +    conn
 +    |> put_status(404)
 +    |> Phoenix.Controller.put_view(Pleroma.Web.ErrorView)
 +    |> Phoenix.Controller.render("404.json")
 +    |> halt()
 +  end
  end
index 7d69e661cdead718c1d9d9d96be670b96a9e374c,deebe48791ceb8cc9185a3c5d9cba020a1f6c2d0..10843b4c8d23ead3cc5b43dad58546be1575c401
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  defmodule Pleroma.Plugs.StaticFEPlug do
@@@ -21,9 -21,6 +21,9 @@@
    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")
 +    conn
 +    |> get_req_header("accept")
 +    |> List.first()
 +    |> String.contains?("text/html")
    end
  end
index 9beaaf8c9d674c6ef9e2df63967c816a9a1d9555,779de0e4d82c2bb6224a27142d273af9b2c4b9f8..525e61360751433889e330f041908dff2127520b
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  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)
  
 +  # Note: some of the following actions (like :update_inbox) may be server-to-server as well
 +  @client_to_server_actions [
 +    :whoami,
 +    :read_inbox,
 +    :outbox,
 +    :update_outbox,
 +    :upload_media,
 +    :followers,
 +    :following
 +  ]
 +
 +  plug(FederatingPlug when action not in @client_to_server_actions)
 +
    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])
  
  
    # 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()})
 +    if FederatingPlug.federating?() do
 +      conn
 +      |> put_resp_content_type("application/activity+json")
 +      |> put_view(UserView)
 +      |> render("following.json", %{user: Relay.get_actor()})
 +    else
 +      FederatingPlug.fail(conn)
 +    end
    end
  
    def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
  
    # 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()})
 +    if FederatingPlug.federating?() do
 +      conn
 +      |> put_resp_content_type("application/activity+json")
 +      |> put_view(UserView)
 +      |> render("followers.json", %{user: Relay.get_actor()})
 +    else
 +      FederatingPlug.fail(conn)
 +    end
    end
  
    def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) 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"
      )
      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
    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
      })
    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
      |> 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 =
      |> json(err)
    end
  
 -  def handle_user_activity(user, %{"type" => "Create"} = params) do
 +  def handle_user_activity(%User{} = user, %{"type" => "Create"} = params) do
      object =
        params["object"]
        |> Map.merge(Map.take(params, ["to", "cc"]))
      })
    end
  
 -  def handle_user_activity(user, %{"type" => "Delete"} = params) do
 +  def 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
      end
    end
  
 -  def handle_user_activity(user, %{"type" => "Like"} = params) do
 +  def 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}
      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,
    - 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,
index 630cd00069d756be09f8bdaa17179a6c2ac469c1,c443c888cac5f467750623fb4bb4bd2fc820ef33..e3f42b5c423cf5001c5148f887fc5e4b54606ec7
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  defmodule Pleroma.Web.OStatus.OStatusController do
@@@ -16,8 -16,6 +16,8 @@@
    alias Pleroma.Web.Metadata.PlayerView
    alias Pleroma.Web.Router
  
 +  plug(Pleroma.Web.FederatingPlug)
 +
    plug(
      RateLimiter,
      [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
index 0eef27c1f94e054dcae1f3c06a419b30f0397bfc,8534a5c136f4bd1f6533b82f27ca3ecb2e9290bd..f74c068cdfac9dde01b30dcaf540db8389c7b061
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  defmodule Pleroma.Plugs.OAuthPlugTest do
@@@ -38,7 -38,7 +38,7 @@@
      assert conn.assigns[:user] == opts[:user]
    end
  
 -  test "with valid token(downcase) in url parameters, it assings the user", opts do
 +  test "with valid token(downcase) in url parameters, it assigns the user", opts do
      conn =
        :get
        |> build_conn("/?access_token=#{opts[:token]}")
index 9c922e9911325439b96bdb8d9bc2e5359e446cad,9151034da1bb229dc4abeb39e860a79e50dd1794..04800b7ea6cb1dd17ee12c6b96f2f6088bc14f01
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
@@@ -25,9 -25,9 +25,9 @@@
      :ok
    end
  
 -  clear_config_all([:instance, :federating],
 -    do: Pleroma.Config.put([:instance, :federating], true)
 -  )
 +  clear_config_all([:instance, :federating]) do
 +    Pleroma.Config.put([:instance, :federating], true)
 +  end
  
    describe "/relay" do
      clear_config([:instance, :allow_relay])
      end
    end
  
 -  describe "/users/:nickname/outbox" do
 +  describe "GET /users/:nickname/outbox" do
      test "it will not bomb when there is no activity", %{conn: conn} do
        user = insert(:user)
  
  
        assert response(conn, 200) =~ announce_activity.data["object"]
      end
 +  end
  
 +  describe "POST /users/:nickname/outbox" do
      test "it rejects posts from other users", %{conn: conn} do
        data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
        user = insert(:user)
        assert result["first"]["orderedItems"] == [user.ap_id]
      end
  
 -    test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
 +    test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do
        user = insert(:user)
        user_two = insert(:user, hide_followers: true)
        User.follow(user, user_two)
      end
    end
  
 -  describe "Additionnal ActivityPub C2S endpoints" do
 +  describe "Additional ActivityPub C2S endpoints" do
      test "/api/ap/whoami", %{conn: conn} do
        user = insert(:user)
  
        assert object["actor"] == user.ap_id
      end
    end
 +
 +  describe "when instance is not federating," do
 +    clear_config([:instance, :federating]) do
 +      Pleroma.Config.put([:instance, :federating], false)
 +    end
 +
 +    test "returns 404 for GET routes", %{conn: conn} do
 +      user = insert(:user)
 +      conn = put_req_header(conn, "accept", "application/json")
 +
 +      get_uris = [
 +        "/users/#{user.nickname}",
 +        "/internal/fetch",
 +        "/relay",
 +        "/relay/following",
 +        "/relay/followers"
 +      ]
 +
 +      for get_uri <- get_uris do
 +        conn
 +        |> get(get_uri)
 +        |> json_response(404)
 +
 +        conn
 +        |> assign(:user, user)
 +        |> get(get_uri)
 +        |> json_response(404)
 +      end
 +    end
 +
 +    test "returns 404 for activity-related POST routes", %{conn: conn} do
 +      user = insert(:user)
 +
 +      conn =
 +        conn
 +        |> assign(:valid_signature, true)
 +        |> put_req_header("content-type", "application/activity+json")
 +
 +      post_activity_data =
 +        "test/fixtures/mastodon-post-activity.json"
 +        |> File.read!()
 +        |> Poison.decode!()
 +
 +      post_activity_uris = [
 +        "/inbox",
 +        "/relay/inbox",
 +        "/users/#{user.nickname}/inbox"
 +      ]
 +
 +      for post_activity_uri <- post_activity_uris do
 +        conn
 +        |> post(post_activity_uri, post_activity_data)
 +        |> json_response(404)
 +
 +        conn
 +        |> assign(:user, user)
 +        |> post(post_activity_uri, post_activity_data)
 +        |> json_response(404)
 +      end
 +    end
 +  end
  end
index 2b7bc662d988ca6b915318ea174143dbd613d379,2051841c28969badf883415d853e8c5b337ec270..725ab1785168c01ad1b69c2420ce2297c5dc82c1
@@@ -1,5 -1,5 +1,5 @@@
  # Pleroma: A lightweight social networking server
- # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  # SPDX-License-Identifier: AGPL-3.0-only
  
  defmodule Pleroma.Web.OStatus.OStatusControllerTest do
               |> response(404)
      end
    end
 +
 +  describe "when instance is not federating," do
 +    clear_config([:instance, :federating]) do
 +      Pleroma.Config.put([:instance, :federating], false)
 +    end
 +
 +    test "returns 404 for GET routes", %{conn: conn} do
 +      conn = put_req_header(conn, "accept", "application/json")
 +
 +      note_activity = insert(:note_activity, local: true)
 +      [_, activity_uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
 +
 +      object = Object.normalize(note_activity)
 +      [_, object_uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
 +
 +      get_uris = [
 +        "/activities/#{activity_uuid}",
 +        "/objects/#{object_uuid}",
 +        "/notice/#{note_activity.id}",
 +        "/notice/#{note_activity.id}/embed_player"
 +      ]
 +
 +      for get_uri <- get_uris do
 +        conn
 +        |> get(get_uri)
 +        |> json_response(404)
 +      end
 +    end
 +  end
  end