From 0bd00de501b36c5f2320c2530019f302bf084517 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 9 Jan 2024 14:49:11 +0100 Subject: [PATCH] fix(backend): only send announcement event emails when the comment author has the right to do so Signed-off-by: Thomas Citharel --- lib/service/activity/conversation.ex | 34 +++- test/service/activity/conversation_test.exs | 177 ++++++++++++++++++-- 2 files changed, 197 insertions(+), 14 deletions(-) diff --git a/lib/service/activity/conversation.ex b/lib/service/activity/conversation.ex index 38eea50e3..5c4301860 100644 --- a/lib/service/activity/conversation.ex +++ b/lib/service/activity/conversation.ex @@ -2,7 +2,7 @@ defmodule Mobilizon.Service.Activity.Conversation do @moduledoc """ Insert a conversation activity """ - alias Mobilizon.Conversations + alias Mobilizon.{Actors, Conversations} alias Mobilizon.Conversations.{Conversation, ConversationParticipant} alias Mobilizon.Discussions.Comment alias Mobilizon.Events.Event @@ -38,7 +38,7 @@ defmodule Mobilizon.Service.Activity.Conversation do %Conversation{ id: conversation_id } = conversation, - %Comment{actor_id: actor_id, text: last_comment_text}, + %Comment{actor_id: actor_id, text: last_comment_text} = comment, _options ) when subject in [ @@ -55,7 +55,8 @@ defmodule Mobilizon.Service.Activity.Conversation do actor_id: conversation_participant_actor_id } = conversation_participant -> - if actor_id != conversation_participant_actor_id do + if actor_id != conversation_participant_actor_id and + can_send_event_announcement?(conversation, comment) do LegacyNotifierBuilder.enqueue( :legacy_notify, %{ @@ -98,4 +99,31 @@ defmodule Mobilizon.Service.Activity.Conversation do } defp event_subject_params(_), do: %{} + + @spec can_send_event_announcement?(Conversation.t(), Comment.t()) :: boolean() + defp can_send_event_announcement?( + %Conversation{ + event: %Event{ + attributed_to_id: attributed_to_id + } + }, + %Comment{actor_id: actor_id} + ) + when not is_nil(attributed_to_id) do + attributed_to_id == actor_id or Actors.member?(actor_id, attributed_to_id) + end + + defp can_send_event_announcement?( + %Conversation{ + event: %Event{ + organizer_actor_id: organizer_actor_id + } + }, + %Comment{actor_id: actor_id} + ) + when not is_nil(organizer_actor_id) do + organizer_actor_id == actor_id + end + + defp can_send_event_announcement?(_, _), do: false end diff --git a/test/service/activity/conversation_test.exs b/test/service/activity/conversation_test.exs index 2639d1fea..97b4fe0aa 100644 --- a/test/service/activity/conversation_test.exs +++ b/test/service/activity/conversation_test.exs @@ -7,6 +7,7 @@ defmodule Mobilizon.Service.Activity.ConversationTest do alias Mobilizon.Conversations alias Mobilizon.Conversations.{Conversation, ConversationParticipant} alias Mobilizon.Discussions.Comment + alias Mobilizon.Events.Event alias Mobilizon.Service.Activity.Conversation, as: ConversationActivity alias Mobilizon.Service.Workers.LegacyNotifierBuilder alias Mobilizon.Users.User @@ -15,16 +16,93 @@ defmodule Mobilizon.Service.Activity.ConversationTest do use Oban.Testing, repo: Mobilizon.Storage.Repo import Mobilizon.Factory - describe "handle conversation" do - test "with participants" do + describe "handle activity from event private announcement conversation" do + test "when conversation initial comment author is not an organizer" do %User{} = user = insert(:user) %Actor{id: actor_id} = actor = insert(:actor, user: user) - %Conversation{ - id: conversation_id, - last_comment: %Comment{actor_id: last_comment_actor_id} - } = - conversation = insert(:conversation, event: nil) + %Actor{} = organizer_actor = insert(:actor) + + %Event{} = event = insert(:event) + + %Comment{} = comment = insert(:comment, actor: organizer_actor) + + %Conversation{id: conversation_id} = + conversation = + insert(:conversation, event: event, last_comment: comment, origin_comment: comment) + + %ConversationParticipant{id: conversation_participant_actor_id} = + insert(:conversation_participant, actor: actor, conversation: conversation) + + %ConversationParticipant{ + id: conversation_participant_id, + actor: %Actor{id: conversation_other_participant_actor_id} + } = insert(:conversation_participant, conversation: conversation) + + conversation = Conversations.get_conversation(conversation_id) + + assert {:ok, _} = + ConversationActivity.insert_activity(conversation, subject: "conversation_created") + + refute_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => organizer_actor.id, + "participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id}, + "object_id" => to_string(conversation_id), + "object_type" => "conversation", + "op" => "legacy_notify", + "subject" => "conversation_created", + "subject_params" => %{ + "conversation_id" => conversation_id, + "conversation_participant_id" => conversation_participant_actor_id, + "conversation_event_id" => event.id, + "conversation_event_title" => event.title, + "conversation_event_uuid" => event.uuid + }, + "type" => "conversation" + } + ) + + refute_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => organizer_actor.id, + "participant" => %{ + "actor_id" => conversation_other_participant_actor_id, + "id" => conversation_participant_id + }, + "object_id" => to_string(conversation_id), + "object_type" => "conversation", + "op" => "legacy_notify", + "subject" => "conversation_created", + "subject_params" => %{ + "conversation_id" => conversation_id, + "conversation_participant_id" => conversation_participant_id, + "conversation_event_id" => event.id, + "conversation_event_title" => event.title, + "conversation_event_uuid" => event.uuid + }, + "type" => "conversation" + } + ) + + assert [] = all_enqueued() + end + + test "an author who is the event organizer" do + %User{} = user = insert(:user) + %Actor{id: actor_id} = actor = insert(:actor, user: user) + + %Actor{} = organizer_actor = insert(:actor) + + %Event{} = event = insert(:event, organizer_actor: organizer_actor) + + %Comment{} = comment = insert(:comment, actor: organizer_actor) + + %Conversation{id: conversation_id} = + conversation = + insert(:conversation, event: event, last_comment: comment, origin_comment: comment) %ConversationParticipant{id: conversation_participant_actor_id} = insert(:conversation_participant, actor: actor, conversation: conversation) @@ -42,7 +120,7 @@ defmodule Mobilizon.Service.Activity.ConversationTest do assert_enqueued( worker: LegacyNotifierBuilder, args: %{ - "author_id" => last_comment_actor_id, + "author_id" => organizer_actor.id, "participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id}, "object_id" => to_string(conversation_id), "object_type" => "conversation", @@ -50,7 +128,10 @@ defmodule Mobilizon.Service.Activity.ConversationTest do "subject" => "conversation_created", "subject_params" => %{ "conversation_id" => conversation_id, - "conversation_participant_id" => conversation_participant_actor_id + "conversation_participant_id" => conversation_participant_actor_id, + "conversation_event_id" => event.id, + "conversation_event_title" => event.title, + "conversation_event_uuid" => event.uuid }, "type" => "conversation" } @@ -59,7 +140,7 @@ defmodule Mobilizon.Service.Activity.ConversationTest do assert_enqueued( worker: LegacyNotifierBuilder, args: %{ - "author_id" => last_comment_actor_id, + "author_id" => organizer_actor.id, "participant" => %{ "actor_id" => conversation_other_participant_actor_id, "id" => conversation_participant_id @@ -70,7 +151,81 @@ defmodule Mobilizon.Service.Activity.ConversationTest do "subject" => "conversation_created", "subject_params" => %{ "conversation_id" => conversation_id, - "conversation_participant_id" => conversation_participant_id + "conversation_participant_id" => conversation_participant_id, + "conversation_event_id" => event.id, + "conversation_event_title" => event.title, + "conversation_event_uuid" => event.uuid + }, + "type" => "conversation" + } + ) + end + + test "an author who is member of the event organizer group" do + %User{} = user = insert(:user) + %Actor{id: actor_id} = actor = insert(:actor, user: user) + + %Actor{} = organizer_group = insert(:group) + + %Event{} = event = insert(:event, attributed_to: organizer_group) + + %Comment{} = comment = insert(:comment, actor: organizer_group) + + %Conversation{id: conversation_id} = + conversation = + insert(:conversation, event: event, last_comment: comment, origin_comment: comment) + + %ConversationParticipant{id: conversation_participant_actor_id} = + insert(:conversation_participant, actor: actor, conversation: conversation) + + %ConversationParticipant{ + id: conversation_participant_id, + actor: %Actor{id: conversation_other_participant_actor_id} + } = insert(:conversation_participant, conversation: conversation) + + conversation = Conversations.get_conversation(conversation_id) + + assert {:ok, _} = + ConversationActivity.insert_activity(conversation, subject: "conversation_created") + + assert_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => organizer_group.id, + "participant" => %{"actor_id" => actor_id, "id" => conversation_participant_actor_id}, + "object_id" => to_string(conversation_id), + "object_type" => "conversation", + "op" => "legacy_notify", + "subject" => "conversation_created", + "subject_params" => %{ + "conversation_id" => conversation_id, + "conversation_participant_id" => conversation_participant_actor_id, + "conversation_event_id" => event.id, + "conversation_event_title" => event.title, + "conversation_event_uuid" => event.uuid + }, + "type" => "conversation" + } + ) + + assert_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => organizer_group.id, + "participant" => %{ + "actor_id" => conversation_other_participant_actor_id, + "id" => conversation_participant_id + }, + "object_id" => to_string(conversation_id), + "object_type" => "conversation", + "op" => "legacy_notify", + "subject" => "conversation_created", + "subject_params" => %{ + "conversation_id" => conversation_id, + "conversation_participant_id" => conversation_participant_id, + "conversation_event_id" => event.id, + "conversation_event_title" => event.title, + "conversation_event_uuid" => event.uuid }, "type" => "conversation" }