From 3bffabccb62a425c46162791957d100c98659ba7 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 8 Mar 2021 11:43:07 +0100 Subject: [PATCH] Only federate group draft posts to members Closes #615 Signed-off-by: Thomas Citharel --- lib/federation/activity_pub/activity_pub.ex | 2 +- lib/federation/activity_pub/audience.ex | 26 +++++++++++------ lib/federation/activity_pub/types/posts.ex | 6 ++-- lib/federation/activity_pub/utils.ex | 8 +++++- .../activity_stream/converter/post.ex | 14 ++++------ test/graphql/resolvers/post_test.exs | 28 +++++++++++++++++++ 6 files changed, 62 insertions(+), 22 deletions(-) diff --git a/lib/federation/activity_pub/activity_pub.ex b/lib/federation/activity_pub/activity_pub.ex index ead2d141c..651aa768a 100644 --- a/lib/federation/activity_pub/activity_pub.ex +++ b/lib/federation/activity_pub/activity_pub.ex @@ -710,7 +710,7 @@ defmodule Mobilizon.Federation.ActivityPub do @spec publish(Actor.t(), Activity.t()) :: :ok def publish(actor, %Activity{recipients: recipients} = activity) do Logger.debug("Publishing an activity") - Logger.debug(inspect(activity)) + Logger.debug(inspect(activity, pretty: true)) public = Visibility.is_public?(activity) Logger.debug("is publicĀ ? #{public}") diff --git a/lib/federation/activity_pub/audience.ex b/lib/federation/activity_pub/audience.ex index 1e491cb3a..f129e59b9 100644 --- a/lib/federation/activity_pub/audience.ex +++ b/lib/federation/activity_pub/audience.ex @@ -123,19 +123,29 @@ defmodule Mobilizon.Federation.ActivityPub.Audience do end def calculate_to_and_cc_from_mentions(%Post{ - attributed_to: %Actor{members_url: members_url}, - visibility: visibility + attributed_to: %Actor{members_url: members_url, followers_url: followers_url}, + visibility: visibility, + draft: draft }) do - case visibility do - :public -> - %{"to" => [@ap_public, members_url], "cc" => []} + cond do + # If the post is draft we send it only to members + draft == true -> + %{"to" => [members_url], "cc" => []} - :unlisted -> - %{"to" => [members_url], "cc" => [@ap_public]} + # If public everyone + visibility == :public -> + %{"to" => [@ap_public, members_url], "cc" => [followers_url]} - :private -> + # Otherwise just followers + visibility == :unlisted -> + %{"to" => [followers_url, members_url], "cc" => [@ap_public]} + + visibility == :private -> # Private is restricted to only the members %{"to" => [members_url], "cc" => []} + + true -> + %{"to" => [], "cc" => []} end end diff --git a/lib/federation/activity_pub/types/posts.ex b/lib/federation/activity_pub/types/posts.ex index 47a9a0341..8421f70d9 100644 --- a/lib/federation/activity_pub/types/posts.ex +++ b/lib/federation/activity_pub/types/posts.ex @@ -24,10 +24,8 @@ defmodule Mobilizon.Federation.ActivityPub.Types.Posts do {:ok, %Actor{} = group} <- Actors.get_group_by_actor_id(group_id), %Actor{} = creator <- Actors.get_actor(creator_id), post_as_data <- - Convertible.model_to_as(%{post | attributed_to: group, author: creator}), - audience <- - Audience.calculate_to_and_cc_from_mentions(post) do - create_data = make_create_data(post_as_data, Map.merge(audience, additional)) + Convertible.model_to_as(%{post | attributed_to: group, author: creator}) do + create_data = make_create_data(post_as_data, additional) {:ok, post, create_data} else diff --git a/lib/federation/activity_pub/utils.ex b/lib/federation/activity_pub/utils.ex index 6d39f3034..988d32333 100644 --- a/lib/federation/activity_pub/utils.ex +++ b/lib/federation/activity_pub/utils.ex @@ -421,7 +421,13 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do ["https://www.w3.org/ns/activitystreams#Public"]} else if actor_type == :Group do - {[actor.followers_url, actor.members_url], []} + to = + (object["to"] || []) + |> MapSet.new() + |> MapSet.intersection(MapSet.new([actor.followers_url, actor.members_url])) + |> MapSet.to_list() + + {to, []} else {[actor.followers_url], []} end diff --git a/lib/federation/activity_stream/converter/post.ex b/lib/federation/activity_stream/converter/post.ex index 45c7ad5d9..79e725d00 100644 --- a/lib/federation/activity_stream/converter/post.ex +++ b/lib/federation/activity_stream/converter/post.ex @@ -7,7 +7,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do """ alias Mobilizon.Actors.Actor alias Mobilizon.Federation.ActivityPub - alias Mobilizon.Federation.ActivityPub.Utils + alias Mobilizon.Federation.ActivityPub.{Audience, Utils} alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Federation.ActivityStream.Converter.Media, as: MediaConverter alias Mobilizon.Posts.Post @@ -36,18 +36,15 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do def model_to_as( %Post{ author: %Actor{url: actor_url}, - attributed_to: %Actor{url: creator_url, followers_url: followers_url} + attributed_to: %Actor{ + url: creator_url + } } = post ) do - to = - if post.visibility == :public, - do: ["https://www.w3.org/ns/activitystreams#Public"], - else: [followers_url] + audience = Audience.calculate_to_and_cc_from_mentions(post) %{ "type" => "Article", - "to" => to, - "cc" => [], "actor" => actor_url, "id" => post.url, "name" => post.title, @@ -56,6 +53,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do "published" => (post.publish_at || post.inserted_at) |> to_date(), "attachment" => [] } + |> Map.merge(audience) |> maybe_add_post_picture(post) |> maybe_add_inline_media(post) end diff --git a/test/graphql/resolvers/post_test.exs b/test/graphql/resolvers/post_test.exs index e20e384b6..968d79303 100644 --- a/test/graphql/resolvers/post_test.exs +++ b/test/graphql/resolvers/post_test.exs @@ -107,6 +107,8 @@ defmodule Mobilizon.GraphQL.Resolvers.PostTest do %Post{} = post_private = insert(:post, attributed_to: group, author: actor, visibility: :private) + insert(:follower, target_actor: group, approved: true) + {:ok, user: user, group: group, @@ -485,6 +487,32 @@ defmodule Mobilizon.GraphQL.Resolvers.PostTest do assert res["data"]["createPost"]["slug"] == "my-post-#{ShortUUID.encode!(id)}" end + test "create_post/3 creates a draft post for a group", %{ + conn: conn, + user: user, + group: group + } do + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @create_post, + variables: %{ + title: @post_title, + body: "My new post is here", + attributedToId: group.id, + draft: true + } + ) + + assert is_nil(res["errors"]) + + assert res["data"]["createPost"]["title"] == @post_title + id = res["data"]["createPost"]["id"] + assert res["data"]["createPost"]["slug"] == "my-post-#{ShortUUID.encode!(id)}" + assert res["data"]["createPost"]["draft"] == true + end + test "create_post/3 doesn't create a post if no group is defined", %{ conn: conn, user: user