From 7b26443a7656163a1ecca6196b745e3393b606f1 Mon Sep 17 00:00:00 2001 From: lain Date: Sat, 17 Feb 2018 16:08:55 +0100 Subject: [PATCH] ActivityPub: Send out Accept after Follow. --- lib/pleroma/user.ex | 5 +++- lib/pleroma/web/activity_pub/activity_pub.ex | 16 +++++++++++- lib/pleroma/web/activity_pub/transmogrifier.ex | 13 +++++++--- lib/pleroma/web/salmon/salmon.ex | 10 +++++++- lib/pleroma/web/websub/websub.ex | 11 ++++++++- test/fixtures/mastodon-accept-activity.json | 34 ++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 test/fixtures/mastodon-accept-activity.json diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index ddf66cee9..61bca3afd 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -150,11 +150,12 @@ defmodule Pleroma.User do def follow(%User{} = follower, %User{info: info} = followed) do ap_followers = followed.follower_address + if following?(follower, followed) or info["deactivated"] do {:error, "Could not follow user: #{followed.nickname} is already on your list."} else - if !followed.local && follower.local do + if !followed.local && follower.local && !ap_enabled?(followed) do Websub.subscribe(follower, followed) end @@ -420,4 +421,6 @@ defmodule Pleroma.User do cs = User.remote_user_creation(data) Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname) end + + def ap_enabled?(%User{info: %{"ap_enabled" => ap}}), do: ap end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8e15fde4a..0c1b6cb28 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -49,6 +49,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + def accept(%{to: to, actor: actor, object: object} = params) do + local = !(params[:local] == false) # only accept false as false value + + with data <- %{"to" => to, "type" => "Accept", "actor" => actor, "object" => object}, + {:ok, activity} <- insert(data, local), + :ok <- maybe_federate(activity) do + {:ok, activity} + end + end + # TODO: This is weird, maybe we shouldn't check here if we can make the activity. def like(%User{ap_id: ap_id} = user, %Object{data: %{"id" => _}} = object, activity_id \\ nil, local \\ true) do with nil <- get_existing_like(ap_id, object), @@ -244,7 +254,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def publish(actor, activity) do - remote_users = Pleroma.Web.Salmon.remote_users(activity) + {:ok, followers} = User.get_followers(actor) + + remote_users = Pleroma.Web.Salmon.remote_users(activity) ++ followers + |> Enum.uniq + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) Enum.each remote_users, fn(user) -> if user.info["ap_enabled"] do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index f4af3aed3..76f04e8a3 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -38,11 +38,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - def handle_incoming(%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id}) do - with %User{} = followed <- User.get_cached_by_ap_id(followed), + def handle_incoming(%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data) do + with %User{local: true} = followed <- User.get_cached_by_ap_id(followed), %User{} = follower <- User.get_or_fetch_by_ap_id(follower), {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do - # TODO: Send an "Accept" activity. + ActivityPub.accept(%{to: [follower.ap_id], actor: followed.ap_id, object: data, local: true}) User.follow(follower, followed) {:ok, activity} else @@ -68,6 +68,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, data} end + def prepare_outgoing(%{"type" => type} = data) when type in ["Follow", "Accept"] do + data = data + |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + + {:ok, data} + end + def add_mention_tags(object) do mentions = object["to"] |> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 81b864582..806f3c3c0 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -154,8 +154,16 @@ defmodule Pleroma.Web.Salmon do defp send_to_user(_,_,_), do: nil + @supported_activities [ + "Create", + "Follow", + "Like", + "Announce", + "Undo", + "Delete" + ] def publish(user, activity, poster \\ &@httpoison.post/4) - def publish(%{info: %{"keys" => keys}} = user, activity, poster) do + def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster) when type in @supported_activities do feed = ActivityRepresenter.to_simple_form(activity, user, true) |> ActivityRepresenter.wrap_with_entry |> :xmerl.export_simple(:xmerl_xml) diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index db1577a93..47a01849d 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -38,7 +38,15 @@ defmodule Pleroma.Web.Websub do end end - def publish(topic, user, activity) do + @supported_activities [ + "Create", + "Follow", + "Like", + "Announce", + "Undo", + "Delete" + ] + def publish(topic, user, %{data: %{"type" => type}} = activity) when type in @supported_activities do # TODO: Only send to still valid subscriptions. query = from sub in WebsubServerSubscription, where: sub.topic == ^topic and sub.state == "active" @@ -58,6 +66,7 @@ defmodule Pleroma.Web.Websub do Pleroma.Web.Federator.enqueue(:publish_single_websub, data) end) end + def publish(_,_,_), do: "" def sign(secret, doc) do :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16 |> String.downcase diff --git a/test/fixtures/mastodon-accept-activity.json b/test/fixtures/mastodon-accept-activity.json new file mode 100644 index 000000000..b661ed6da --- /dev/null +++ b/test/fixtures/mastodon-accept-activity.json @@ -0,0 +1,34 @@ +{ + "type": "Accept", + "signature": { + "type": "RsaSignature2017", + "signatureValue": "rBzK4Kqhd4g7HDS8WE5oRbWQb2R+HF/6awbUuMWhgru/xCODT0SJWSri0qWqEO4fPcpoUyz2d25cw6o+iy9wiozQb3hQNnu69AR+H5Mytc06+g10KCHexbGhbAEAw/7IzmeXELHUbaqeduaDIbdt1zw4RkwLXdqgQcGXTJ6ND1wM3WMHXQCK1m0flasIXFoBxpliPAGiElV8s0+Ltuh562GvflG3kB3WO+j+NaR0ZfG5G9N88xMj9UQlCKit5gpAE5p6syUsCU2WGBHywTumv73i3OVTIFfq+P9AdMsRuzw1r7zoKEsthW4aOzLQDi01ZjvdBz8zH6JnjDU7SMN/Ig==", + "creator": "http://mastodon.example.org/users/admin#main-key", + "created": "2018-02-17T14:36:41Z" + }, + "object": { + "type": "Follow", + "object": "http://mastodon.example.org/users/admin", + "id": "http://localtesting.pleroma.lol/users/lain#follows/4", + "actor": "http://localtesting.pleroma.lol/users/lain" + }, + "nickname": "lain", + "id": "http://mastodon.example.org/users/admin#accepts/follows/4", + "actor": "http://mastodon.example.org/users/admin", + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "toot": "http://joinmastodon.org/ns#", + "sensitive": "as:sensitive", + "ostatus": "http://ostatus.org#", + "movedTo": "as:movedTo", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "atomUri": "ostatus:atomUri", + "Hashtag": "as:Hashtag", + "Emoji": "toot:Emoji" + } + ] +} \ No newline at end of file