MRF / Dropping of incoming/outgoing messages based on allow-list

This commit is contained in:
Eliot Berriot 2019-06-20 10:27:07 +02:00
parent 371dc01205
commit 08262529ec
24 changed files with 897 additions and 89 deletions

View file

@ -13,10 +13,13 @@ from funkwhale_api.federation import (
)
def test_receive_validates_basic_attributes_and_stores_activity(factories, now, mocker):
def test_receive_validates_basic_attributes_and_stores_activity(
mrf_inbox_registry, factories, now, mocker
):
mocker.patch.object(
activity.InboxRouter, "get_matching_handlers", return_value=True
)
mrf_inbox_registry_apply = mocker.spy(mrf_inbox_registry, "apply")
mocked_dispatch = mocker.patch("funkwhale_api.common.utils.on_commit")
local_to_actor = factories["users.User"]().create_actor()
local_cc_actor = factories["users.User"]().create_actor()
@ -31,6 +34,7 @@ def test_receive_validates_basic_attributes_and_stores_activity(factories, now,
}
copy = activity.receive(activity=a, on_behalf_of=remote_actor)
mrf_inbox_registry_apply.assert_called_once_with(a, sender_id=a["actor"])
assert copy.payload == a
assert copy.creation_date >= now
@ -49,6 +53,63 @@ def test_receive_validates_basic_attributes_and_stores_activity(factories, now,
assert ii.is_read is False
def test_receive_uses_mrf_returned_payload(mrf_inbox_registry, factories, now, mocker):
mocker.patch.object(
activity.InboxRouter, "get_matching_handlers", return_value=True
)
def patched_apply(payload, **kwargs):
payload["type"] = "SomethingElse"
return payload, True
mrf_inbox_registry_apply = mocker.patch.object(
mrf_inbox_registry, "apply", side_effect=patched_apply
)
mocked_dispatch = mocker.patch("funkwhale_api.common.utils.on_commit")
local_to_actor = factories["users.User"]().create_actor()
remote_actor = factories["federation.Actor"]()
a = {
"@context": [],
"actor": remote_actor.fid,
"type": "Noop",
"id": "https://test.activity",
"to": [local_to_actor.fid],
}
copy = activity.receive(activity=a, on_behalf_of=remote_actor)
mrf_inbox_registry_apply.assert_called_once_with(a, sender_id=a["actor"])
expected = a.copy()
expected["type"] = "SomethingElse"
assert copy.payload == expected
assert copy.creation_date >= now
assert copy.actor == remote_actor
assert copy.fid == a["id"]
assert copy.type == "SomethingElse"
mocked_dispatch.assert_called_once_with(
tasks.dispatch_inbox.delay, activity_id=copy.pk
)
def test_receive_mrf_skip(mrf_inbox_registry, factories, now, mocker):
mocker.patch.object(
activity.InboxRouter, "get_matching_handlers", return_value=True
)
mocker.patch.object(mrf_inbox_registry, "apply", return_value=(None, False))
local_to_actor = factories["users.User"]().create_actor()
remote_actor = factories["federation.Actor"]()
a = {
"@context": [],
"actor": remote_actor.fid,
"type": "Noop",
"id": "https://test.activity",
"to": [local_to_actor.fid],
}
copy = activity.receive(activity=a, on_behalf_of=remote_actor)
assert copy is None
def test_receive_calls_should_reject(factories, now, mocker):
should_reject = mocker.patch.object(activity, "should_reject", return_value=True)
mocker.patch.object(
@ -287,7 +348,7 @@ def test_route_matching(route, payload, expected):
assert activity.match_route(route, payload) is expected
def test_outbox_router_dispatch(mocker, factories, now):
def test_outbox_router_dispatch(mocker, factories, preferences, now):
router = activity.OutboxRouter()
actor = factories["federation.Actor"]()
r1 = factories["federation.Actor"]()
@ -331,7 +392,39 @@ def test_outbox_router_dispatch(mocker, factories, now):
assert delivery.is_delivered is False
def test_prepare_deliveries_and_inbox_items(factories):
def test_outbox_router_dispatch_allow_list(mocker, factories, preferences, now):
preferences["moderation__allow_list_enabled"] = True
router = activity.OutboxRouter()
actor = factories["federation.Actor"]()
r1 = factories["federation.Actor"](domain__allowed=True)
r2 = factories["federation.Actor"]()
prepare_deliveries_and_inbox_items = mocker.spy(
activity, "prepare_deliveries_and_inbox_items"
)
def handler(context):
yield {
"payload": {
"type": "Noop",
"actor": actor.fid,
"summary": context["summary"],
"to": [r1],
"cc": [r2],
},
"actor": actor,
}
router.connect({"type": "Noop"}, handler)
router.dispatch({"type": "Noop"}, {"summary": "hello"})
prepare_deliveries_and_inbox_items.assert_any_call(
[r1], "to", allowed_domains=set([r1.domain_id])
)
prepare_deliveries_and_inbox_items.assert_any_call(
[r2], "cc", allowed_domains=set([r1.domain_id])
)
def test_prepare_deliveries_and_inbox_items(factories, preferences):
local_actor1 = factories["federation.Actor"](
local=True, shared_inbox_url="https://testlocal.inbox"
)
@ -385,7 +478,7 @@ def test_prepare_deliveries_and_inbox_items(factories):
]
inbox_items, deliveries, urls = activity.prepare_deliveries_and_inbox_items(
recipients, "to"
recipients, "to", allowed_domains=None
)
expected_inbox_items = sorted(
[
@ -436,6 +529,32 @@ def test_prepare_deliveries_and_inbox_items(factories):
assert inbox_item.type == "to"
def test_prepare_deliveries_and_inbox_items_allow_list(factories, preferences):
preferences["moderation__allow_list_enabled"] = True
remote_actor1 = factories["federation.Actor"](domain__allowed=True)
remote_actor2 = factories["federation.Actor"](domain__allowed=False)
recipients = [remote_actor1, remote_actor2]
inbox_items, deliveries, urls = activity.prepare_deliveries_and_inbox_items(
recipients, "to", allowed_domains=set([remote_actor1.domain_id])
)
expected_inbox_items = []
expected_deliveries = [models.Delivery(inbox_url=remote_actor1.inbox_url)]
expected_urls = [remote_actor1.fid]
assert urls == expected_urls
assert len(expected_inbox_items) == len(inbox_items)
assert len(expected_deliveries) == len(deliveries)
for delivery, expected_delivery in zip(
sorted(deliveries, key=lambda v: v.inbox_url), expected_deliveries
):
assert delivery.inbox_url == expected_delivery.inbox_url
def test_prepare_deliveries_and_inbox_items_instances_with_followers(factories):
domain1 = factories["federation.Domain"](with_service_actor=True)
@ -460,7 +579,7 @@ def test_prepare_deliveries_and_inbox_items_instances_with_followers(factories):
recipients = [activity.PUBLIC_ADDRESS, {"type": "instances_with_followers"}]
inbox_items, deliveries, urls = activity.prepare_deliveries_and_inbox_items(
recipients, "to"
recipients, "to", allowed_domains=None
)
expected_deliveries = sorted(
@ -483,6 +602,20 @@ def test_prepare_deliveries_and_inbox_items_instances_with_followers(factories):
assert delivery.inbox_url == expected_delivery.inbox_url
@pytest.mark.parametrize(
"url, allowed_domains, expected",
[
("https://domain.example/test", None, True),
("https://domain.example/test", [], False),
("https://allowed.example/test", ["allowed.example"], True),
("https://domain.example/test", ["allowed.example"], False),
("https://social.allowed.example/test", ["allowed.example"], False),
],
)
def test_is_allowed_url(url, allowed_domains, expected):
assert activity.is_allowed_url(url, allowed_domains) is expected
def test_should_rotate_actor_key(settings, cache, now):
actor_id = 42
settings.ACTOR_KEY_ROTATION_DELAY = 10
@ -507,7 +640,9 @@ def test_schedule_key_rotation(cache, mocker):
assert cache.get(activity.ACTOR_KEY_ROTATION_LOCK_CACHE_KEY.format(actor_id), True)
def test_outbox_dispatch_rotate_key_on_delete(mocker, factories, cache, settings):
def test_outbox_dispatch_rotate_key_on_delete(
mocker, factories, cache, settings, preferences
):
router = activity.OutboxRouter()
actor = factories["federation.Actor"]()
r1 = factories["federation.Actor"]()