Allow to use inline "Join" when processing an Accept

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-07-30 17:25:45 +02:00
parent 099b85e9a9
commit 4b864ba423
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
2 changed files with 75 additions and 9 deletions

View File

@ -837,7 +837,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
# Handle incoming `Accept` activities wrapping a `Join` activity on an event # Handle incoming `Accept` activities wrapping a `Join` activity on an event
defp do_handle_incoming_accept_join(join_object, %Actor{} = actor_accepting) do defp do_handle_incoming_accept_join(join_object, %Actor{} = actor_accepting) do
case get_participant(join_object) do case get_participant(join_object, actor_accepting) do
{:ok, participant} -> {:ok, participant} ->
do_handle_incoming_accept_join_event(participant, actor_accepting) do_handle_incoming_accept_join_event(participant, actor_accepting)
@ -868,9 +868,9 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
%Actor{} = actor_accepting %Actor{} = actor_accepting
) )
when role in [:not_approved, :rejected] do when role in [:not_approved, :rejected] do
# TODO: The actor that accepts the Join activity may another one that the event organizer ? with %Event{} = event <- Events.get_event_with_preload!(event.id),
# Or maybe for groups it's the group that sends the Accept activity {:can_accept_event_join, true} <-
with {:same_actor, true} <- {:same_actor, actor_accepting.id == event.organizer_actor_id}, {:can_accept_event_join, can_accept_event_join?(actor_accepting, event)},
{:ok, %Activity{} = activity, %Participant{role: :participant} = participant} <- {:ok, %Activity{} = activity, %Participant{role: :participant} = participant} <-
ActivityPub.accept( ActivityPub.accept(
:join, :join,
@ -881,8 +881,8 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
Participation.send_emails_to_local_user(participant) do Participation.send_emails_to_local_user(participant) do
{:ok, activity, participant} {:ok, activity, participant}
else else
{:same_actor} -> {:can_accept_event_join, false} ->
{:error, "Actor who accepted the join wasn't the event organizer. Quite odd."} {:error, "Actor who accepted the join didn't have permission to do so."}
{:ok, %Participant{role: :participant} = _follow} -> {:ok, %Participant{role: :participant} = _follow} ->
{:error, "Participant"} {:error, "Participant"}
@ -917,7 +917,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
defp do_handle_incoming_reject_join(join_object, %Actor{} = actor_accepting) do defp do_handle_incoming_reject_join(join_object, %Actor{} = actor_accepting) do
with {:join_event, {:ok, %Participant{event: event, role: role} = participant}} with {:join_event, {:ok, %Participant{event: event, role: role} = participant}}
when role != :rejected <- when role != :rejected <-
{:join_event, get_participant(join_object)}, {:join_event, get_participant(join_object, actor_accepting)},
# TODO: The actor that accepts the Join activity may another one that the event organizer ? # TODO: The actor that accepts the Join activity may another one that the event organizer ?
# Or maybe for groups it's the group that sends the Accept activity # Or maybe for groups it's the group that sends the Accept activity
{:same_actor, true} <- {:same_actor, actor_accepting.id == event.organizer_actor_id}, {:same_actor, true} <- {:same_actor, actor_accepting.id == event.organizer_actor_id},
@ -1025,14 +1025,22 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end end
end end
defp get_participant(join_object) do defp get_participant(join_object, %Actor{} = actor_accepting, loop \\ 1) do
with join_object_id when not is_nil(join_object_id) <- Utils.get_url(join_object), with join_object_id when not is_nil(join_object_id) <- Utils.get_url(join_object),
{:not_found, %Participant{} = participant} <- {:not_found, %Participant{} = participant} <-
{:not_found, Events.get_participant_by_url(join_object_id)} do {:not_found, Events.get_participant_by_url(join_object_id)} do
{:ok, participant} {:ok, participant}
else else
{:not_found, _err} -> {:not_found, _err} ->
with true <- is_map(join_object),
true <- loop < 2,
true <- Utils.are_same_origin?(actor_accepting.url, join_object["id"]),
{:ok, _activity, %Participant{url: participant_url}} <- handle_incoming(join_object) do
get_participant(participant_url, actor_accepting, 2)
else
_ ->
{:error, "Participant URL not found"} {:error, "Participant URL not found"}
end
_ -> _ ->
{:error, "ActivityPub ID not found in Accept Join object"} {:error, "ActivityPub ID not found in Accept Join object"}
@ -1129,4 +1137,22 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
_ -> {:error, :remove_object_not_found} _ -> {:error, :remove_object_not_found}
end end
end end
defp can_accept_event_join?(
%Actor{url: actor_url} = actor,
%Event{attributed_to: %Actor{type: :Group, url: group_url} = _group} = event
) do
actor_url == group_url || Permission.can_update_group_object?(actor, event)
end
defp can_accept_event_join?(
%Actor{id: actor_id},
%Event{organizer_actor: %Actor{id: organizer_actor_id}}
) do
organizer_actor_id == actor_id
end
defp can_accept_event_join?(_actor, _event) do
false
end
end end

View File

@ -64,6 +64,46 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.JoinTest do
# We don't accept already accepted Accept activities # We don't accept already accepted Accept activities
:error = Transmogrifier.handle_incoming(accept_data) :error = Transmogrifier.handle_incoming(accept_data)
end end
test "it accepts Accept activities with an inline Join from same origin" do
%Actor{} = organizer = insert(:actor)
%Actor{url: participant_actor_url} =
insert(:actor, domain: "somewhere.else", url: "https://somewhere.else/@participant")
%Actor{} =
group = insert(:group, domain: "somewhere.else", url: "https://somewhere.else/@group")
insert(:member, actor: organizer, parent: group, role: :moderator)
%Actor{} =
actor_member_2 =
insert(:actor, domain: "somewhere.else", url: "https://somewhere.else/@member")
insert(:member, actor: actor_member_2, parent: group, role: :moderator)
%Event{url: event_url} =
insert(:event, organizer_actor: organizer, join_options: :restricted, attributed_to: group)
join_data =
File.read!("test/fixtures/mobilizon-join-activity.json")
|> Jason.decode!()
|> Map.put("actor", participant_actor_url)
|> Map.put("object", event_url)
|> Map.put("participationMessage", @join_message)
|> Map.put("id", "https://somewhere.else/@participant/join/event/1")
accept_data =
File.read!("test/fixtures/mastodon-accept-activity.json")
|> Jason.decode!()
|> Map.put("actor", actor_member_2.url)
|> Map.put("object", join_data)
{:ok, accept_activity, _} = Transmogrifier.handle_incoming(accept_data)
assert accept_activity.data["object"]["id"] == join_data["id"]
assert accept_activity.data["object"]["id"] =~ "/join/"
assert accept_activity.data["id"] =~ "/accept/join/"
end
end end
describe "handle incoming reject join activities" do describe "handle incoming reject join activities" do