activitypub: publisher: align sharedinbox usage with AP specification rules
While debugging the follow breakage, I observed that our sharedInbox usage did not match the rules in the specification. Accordingly, I have better aligned our usage of sharedInbox with the rules outlined in the ActivityPub specification.
This commit is contained in:
parent
a00aab3402
commit
56019d53a8
|
@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
|
- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
|
||||||
- Existing user id not being preserved on insert conflict
|
- Existing user id not being preserved on insert conflict
|
||||||
- Rich Media: Parser failing when no TTL can be found by image TTL setters
|
- Rich Media: Parser failing when no TTL can be found by image TTL setters
|
||||||
|
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
|
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
|
||||||
|
|
|
@ -112,6 +112,45 @@ defp get_cc_ap_ids(ap_id, recipients) do
|
||||||
|> Enum.map(& &1.ap_id)
|
|> Enum.map(& &1.ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@as_public "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
defp maybe_use_sharedinbox(%User{info: %{source_data: data}}),
|
||||||
|
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Determine a user inbox to use based on heuristics. These heuristics
|
||||||
|
are based on an approximation of the ``sharedInbox`` rules in the
|
||||||
|
[ActivityPub specification][ap-sharedinbox].
|
||||||
|
|
||||||
|
Please do not edit this function (or its children) without reading
|
||||||
|
the spec, as editing the code is likely to introduce some breakage
|
||||||
|
without some familiarity.
|
||||||
|
|
||||||
|
[ap-sharedinbox]: https://www.w3.org/TR/activitypub/#shared-inbox-delivery
|
||||||
|
"""
|
||||||
|
def determine_inbox(
|
||||||
|
%Activity{data: activity_data},
|
||||||
|
%User{info: %{source_data: data}} = user
|
||||||
|
) do
|
||||||
|
to = activity_data["to"] || []
|
||||||
|
cc = activity_data["cc"] || []
|
||||||
|
type = activity_data["type"]
|
||||||
|
|
||||||
|
cond do
|
||||||
|
type == "Delete" ->
|
||||||
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
|
@as_public in to || @as_public in cc ->
|
||||||
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
|
length(to) + length(cc) > 1 ->
|
||||||
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
data["inbox"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Publishes an activity with BCC to all relevant peers.
|
Publishes an activity with BCC to all relevant peers.
|
||||||
"""
|
"""
|
||||||
|
@ -166,8 +205,8 @@ def publish(%User{} = actor, %Activity{} = activity) do
|
||||||
|
|
||||||
recipients(actor, activity)
|
recipients(actor, activity)
|
||||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||||
|> Enum.map(fn %{info: %{source_data: data}} ->
|
|> Enum.map(fn %User{} = user ->
|
||||||
(is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
determine_inbox(activity, user)
|
||||||
end)
|
end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||||
|
|
112
test/web/activity_pub/publisher_test.exs
Normal file
112
test/web/activity_pub/publisher_test.exs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Web.ActivityPub.Publisher
|
||||||
|
|
||||||
|
@as_public "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
describe "determine_inbox/2" do
|
||||||
|
test "it returns sharedInbox for messages involving as:Public in to" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [@as_public], "cc" => [user.follower_address]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving as:Public in cc" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"cc" => [@as_public], "to" => [user.follower_address]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving multiple recipients in to" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
user_two = insert(:user)
|
||||||
|
user_three = insert(:user)
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving multiple recipients in cc" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
user_two = insert(:user)
|
||||||
|
user_three = insert(:user)
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving multiple recipients in total" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{
|
||||||
|
source_data: %{
|
||||||
|
"inbox" => "http://example.com/personal-inbox",
|
||||||
|
"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns inbox for messages involving single recipients in total" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{
|
||||||
|
source_data: %{
|
||||||
|
"inbox" => "http://example.com/personal-inbox",
|
||||||
|
"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [user.ap_id], "cc" => []}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue