diff options
| -rw-r--r-- | lib/pleroma/object/fetcher.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/signature.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub_controller.ex | 44 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/publisher.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/relay.ex | 28 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 13 | ||||
| -rw-r--r-- | test/signature_test.exs | 14 | ||||
| -rw-r--r-- | test/tasks/relay_test.exs | 4 | ||||
| -rw-r--r-- | test/web/activity_pub/activity_pub_controller_test.exs | 29 | ||||
| -rw-r--r-- | test/web/activity_pub/relay_test.exs | 39 | 
10 files changed, 144 insertions, 41 deletions
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 8d79ddb1f..c1795ae0f 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -117,9 +117,7 @@ defmodule Pleroma.Object.Fetcher do    def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do      Logger.info("Fetching object #{id} via AP") -    date = -      NaiveDateTime.utc_now() -      |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") +    date = Pleroma.Signature.signed_date()      headers =        [{:Accept, "application/activity+json"}] diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 15bf3c317..f20aeb0d5 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -53,4 +53,10 @@ defmodule Pleroma.Signature do        HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)      end    end + +  def signed_date, do: signed_date(NaiveDateTime.utc_now()) + +  def signed_date(%NaiveDateTime{} = date) do +    Timex.format!(date, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") +  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 133a726c5..ed801a7ae 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -41,7 +41,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {:ok, user} <- User.ensure_keys_present(user) do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("user.json", %{user: user}))      else        nil -> {:error, :not_found} @@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do           %Object{} = object <- Object.get_cached_by_ap_id(ap_id),           {_, true} <- {:public?, Visibility.is_public?(object)} do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(ObjectView.render("object.json", %{object: object}))      else        {:public?, false} -> @@ -69,7 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do        {page, _} = Integer.parse(page)        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(ObjectView.render("likes.json", ap_id, likes, page))      else        {:public?, false} -> @@ -83,7 +83,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do           {_, true} <- {:public?, Visibility.is_public?(object)},           likes <- Utils.get_object_likes(object) do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(ObjectView.render("likes.json", ap_id, likes))      else        {:public?, false} -> @@ -96,7 +96,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do           %Activity{} = activity <- Activity.normalize(ap_id),           {_, true} <- {:public?, Visibility.is_public?(activity)} do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(ObjectView.render("object.json", %{object: activity}))      else        {:public?, false} -> @@ -104,6 +104,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      end    end +  # GET /relay/following +  def following(%{assigns: %{relay: true}} = conn, _params) do +    conn +    |> put_resp_content_type("application/activity+json") +    |> json(UserView.render("following.json", %{user: Relay.get_actor()})) +  end +    def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), @@ -112,12 +119,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do        {page, _} = Integer.parse(page)        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("following.json", %{user: user, page: page, for: for_user}))      else        {:show_follows, _} ->          conn -        |> put_resp_header("content-type", "application/activity+json") +        |> put_resp_content_type("application/activity+json")          |> send_resp(403, "")      end    end @@ -126,11 +133,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("following.json", %{user: user, for: for_user}))      end    end +  # GET /relay/followers +  def followers(%{assigns: %{relay: true}} = conn, _params) do +    conn +    |> put_resp_content_type("application/activity+json") +    |> json(UserView.render("followers.json", %{user: Relay.get_actor()})) +  end +    def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user), @@ -139,12 +153,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do        {page, _} = Integer.parse(page)        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("followers.json", %{user: user, page: page, for: for_user}))      else        {:show_followers, _} ->          conn -        |> put_resp_header("content-type", "application/activity+json") +        |> put_resp_content_type("application/activity+json")          |> send_resp(403, "")      end    end @@ -153,7 +167,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("followers.json", %{user: user, for: for_user}))      end    end @@ -162,7 +176,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      with %User{} = user <- User.get_cached_by_nickname(nickname),           {:ok, user} <- User.ensure_keys_present(user) do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))      end    end @@ -210,7 +224,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    defp represent_service_actor(%User{} = user, conn) do      with {:ok, user} <- User.ensure_keys_present(user) do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("user.json", %{user: user}))      else        nil -> {:error, :not_found} @@ -231,7 +245,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    def whoami(%{assigns: %{user: %User{} = user}} = conn, _params) do      conn -    |> put_resp_header("content-type", "application/activity+json") +    |> put_resp_content_type("application/activity+json")      |> json(UserView.render("user.json", %{user: user}))    end @@ -240,7 +254,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do      if nickname == user.nickname do        conn -      |> put_resp_header("content-type", "application/activity+json") +      |> put_resp_content_type("application/activity+json")        |> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]}))      else        err = diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 262529b84..c97405690 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -50,9 +50,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do      digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) -    date = -      NaiveDateTime.utc_now() -      |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") +    date = Pleroma.Signature.signed_date()      signature =        Pleroma.Signature.sign(actor, %{ diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 5f18cc64a..c2ac38907 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -22,13 +22,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do        Logger.info("relay: followed instance: #{target_instance}; id=#{activity.data["id"]}")        {:ok, activity}      else -      {:error, _} = error -> -        Logger.error("error: #{inspect(error)}") -        error - -      e -> -        Logger.error("error: #{inspect(e)}") -        {:error, e} +      error -> format_error(error)      end    end @@ -37,16 +31,11 @@ defmodule Pleroma.Web.ActivityPub.Relay do      with %User{} = local_user <- get_actor(),           {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_instance),           {:ok, activity} <- ActivityPub.unfollow(local_user, target_user) do +      User.unfollow(local_user, target_user)        Logger.info("relay: unfollowed instance: #{target_instance}: id=#{activity.data["id"]}")        {:ok, activity}      else -      {:error, _} = error -> -        Logger.error("error: #{inspect(error)}") -        error - -      e -> -        Logger.error("error: #{inspect(e)}") -        {:error, e} +      error -> format_error(error)      end    end @@ -56,11 +45,16 @@ defmodule Pleroma.Web.ActivityPub.Relay do           %Object{} = object <- Object.normalize(activity) do        ActivityPub.announce(user, object, nil, true, false)      else -      e -> -        Logger.error("error: #{inspect(e)}") -        {:error, inspect(e)} +      error -> format_error(error)      end    end    def publish(_), do: {:error, "Not implemented"} + +  defp format_error({:error, error}), do: format_error(error) + +  defp format_error(error) do +    Logger.error("error: #{inspect(error)}") +    {:error, error} +  end  end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index f800d16fd..1ad33630c 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -133,6 +133,10 @@ defmodule Pleroma.Web.Router do      })    end +  pipeline :http_signature do +    plug(Pleroma.Web.Plugs.HTTPSignaturePlug) +  end +    scope "/api/pleroma", Pleroma.Web.TwitterAPI do      pipe_through(:pleroma_api) @@ -688,7 +692,14 @@ defmodule Pleroma.Web.Router do      pipe_through(:ap_service_actor)      get("/", ActivityPubController, :relay) -    post("/inbox", ActivityPubController, :inbox) + +    scope [] do +      pipe_through(:http_signature) +      post("/inbox", ActivityPubController, :inbox) +    end + +    get("/following", ActivityPubController, :following, assigns: %{relay: true}) +    get("/followers", ActivityPubController, :followers, assigns: %{relay: true})    end    scope "/internal/fetch", Pleroma.Web.ActivityPub do diff --git a/test/signature_test.exs b/test/signature_test.exs index 26337eaf9..d5bf63d7d 100644 --- a/test/signature_test.exs +++ b/test/signature_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.SignatureTest do    import ExUnit.CaptureLog    import Pleroma.Factory    import Tesla.Mock +  import Mock    alias Pleroma.Signature @@ -114,4 +115,17 @@ defmodule Pleroma.SignatureTest do                 "https://example.com/users/1234"      end    end + +  describe "signed_date" do +    test "it returns formatted current date" do +      with_mock(NaiveDateTime, utc_now: fn -> ~N[2019-08-23 18:11:24.822233] end) do +        assert Signature.signed_date() == "Fri, 23 Aug 2019 18:11:24 GMT" +      end +    end + +    test "it returns formatted date" do +      assert Signature.signed_date(~N[2019-08-23 08:11:24.822233]) == +               "Fri, 23 Aug 2019 08:11:24 GMT" +    end +  end  end diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index 0d341c8d6..7bde56606 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -50,7 +50,8 @@ defmodule Mix.Tasks.Pleroma.RelayTest do        %User{ap_id: follower_id} = local_user = Relay.get_actor()        target_user = User.get_cached_by_ap_id(target_instance)        follow_activity = Utils.fetch_latest_follow(local_user, target_user) - +      User.follow(local_user, target_user) +      assert "#{target_instance}/followers" in refresh_record(local_user).following        Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance])        cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) @@ -67,6 +68,7 @@ defmodule Mix.Tasks.Pleroma.RelayTest do        assert undo_activity.data["type"] == "Undo"        assert undo_activity.data["actor"] == local_user.ap_id        assert undo_activity.data["object"] == cancelled_activity.data +      refute "#{target_instance}/followers" in refresh_record(local_user).following      end    end diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 77f5e39fa..5192e734f 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do    alias Pleroma.Object    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ObjectView +  alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.ActivityPub.UserView    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.CommonAPI @@ -593,6 +594,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do      end    end +  describe "/relay/followers" do +    test "it returns relay followers", %{conn: conn} do +      relay_actor = Relay.get_actor() +      user = insert(:user) +      User.follow(user, relay_actor) + +      result = +        conn +        |> assign(:relay, true) +        |> get("/relay/followers") +        |> json_response(200) + +      assert result["first"]["orderedItems"] == [user.ap_id] +    end +  end + +  describe "/relay/following" do +    test "it returns relay following", %{conn: conn} do +      result = +        conn +        |> assign(:relay, true) +        |> get("/relay/following") +        |> json_response(200) + +      assert result["first"]["orderedItems"] == [] +    end +  end +    describe "/users/:nickname/followers" do      test "it returns the followers in a collection", %{conn: conn} do        user = insert(:user) diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index e10b808f7..4f7d592a6 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do    alias Pleroma.Web.ActivityPub.Relay    import Pleroma.Factory +  import Mock    test "gets an actor for the relay" do      user = Relay.get_actor() @@ -43,16 +44,21 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do        user = insert(:user)        service_actor = Relay.get_actor()        ActivityPub.follow(service_actor, user) +      Pleroma.User.follow(service_actor, user) +      assert "#{user.ap_id}/followers" in refresh_record(service_actor).following        assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id)        assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay"        assert user.ap_id in activity.recipients        assert activity.data["type"] == "Undo"        assert activity.data["actor"] == service_actor.ap_id        assert activity.data["to"] == [user.ap_id] +      refute "#{user.ap_id}/followers" in refresh_record(service_actor).following      end    end    describe "publish/1" do +    clear_config([:instance, :federating]) +      test "returns error when activity not `Create` type" do        activity = insert(:like_activity)        assert Relay.publish(activity) == {:error, "Not implemented"} @@ -63,13 +69,44 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do        assert Relay.publish(activity) == {:error, false}      end -    test "returns announce activity" do +    test "returns error when object is unknown" do +      activity = +        insert(:note_activity, +          data: %{ +            "type" => "Create", +            "object" => "http://mastodon.example.org/eee/99541947525187367" +          } +        ) + +      assert Relay.publish(activity) == {:error, nil} +    end + +    test_with_mock "returns announce activity and publish to federate", +                   Pleroma.Web.Federator, +                   [:passthrough], +                   [] do +      Pleroma.Config.put([:instance, :federating], true) +      service_actor = Relay.get_actor() +      note = insert(:note_activity) +      assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note) +      assert activity.data["type"] == "Announce" +      assert activity.data["actor"] == service_actor.ap_id +      assert activity.data["object"] == obj.data["id"] +      assert called(Pleroma.Web.Federator.publish(activity, 5)) +    end + +    test_with_mock "returns announce activity and not publish to federate", +                   Pleroma.Web.Federator, +                   [:passthrough], +                   [] do +      Pleroma.Config.put([:instance, :federating], false)        service_actor = Relay.get_actor()        note = insert(:note_activity)        assert {:ok, %Activity{} = activity, %Object{} = obj} = Relay.publish(note)        assert activity.data["type"] == "Announce"        assert activity.data["actor"] == service_actor.ap_id        assert activity.data["object"] == obj.data["id"] +      refute called(Pleroma.Web.Federator.publish(activity, 5))      end    end  end  | 
