logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma
commit: 6dc24422dc403663f6385272f071e2223c24b2ce
parent: 897bd7a15e6fef5e26cb207e14d36ee06ceb0995
Author: kaniini <ariadne@dereferenced.org>
Date:   Sun, 25 Aug 2019 19:43:27 +0000

Merge branch 'issue/1177' into 'develop'

[#1177] fixed unfollow for relay actor

See merge request pleroma/pleroma!1589

Diffstat:

Mlib/pleroma/object/fetcher.ex4+---
Mlib/pleroma/signature.ex6++++++
Mlib/pleroma/web/activity_pub/activity_pub_controller.ex44+++++++++++++++++++++++++++++---------------
Mlib/pleroma/web/activity_pub/publisher.ex4+---
Mlib/pleroma/web/activity_pub/relay.ex28+++++++++++-----------------
Mlib/pleroma/web/router.ex13++++++++++++-
Mtest/signature_test.exs14++++++++++++++
Mtest/tasks/relay_test.exs4+++-
Mtest/web/activity_pub/activity_pub_controller_test.exs29+++++++++++++++++++++++++++++
Mtest/web/activity_pub/relay_test.exs39++++++++++++++++++++++++++++++++++++++-
10 files changed, 144 insertions(+), 41 deletions(-)

diff --git 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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