user_groups: Send metadata access related events on add members.

This commit is contained in:
Shubham Padia
2025-03-20 14:02:51 +00:00
committed by Tim Abbott
parent b62d51f0ae
commit 8c069674b0
4 changed files with 108 additions and 3 deletions

View File

@@ -8,12 +8,21 @@ from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
from zerver.lib.stream_subscription import get_user_ids_for_streams
from zerver.lib.stream_traffic import get_streams_traffic
from zerver.lib.streams import (
bulk_can_access_stream_metadata_user_ids,
get_anonymous_group_membership_dict_for_streams,
get_metadata_access_streams_via_group_ids,
send_stream_creation_event,
)
from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.timestamp import datetime_to_timestamp
from zerver.lib.types import UserGroupMembersData, UserGroupMembersDict from zerver.lib.types import UserGroupMembersData, UserGroupMembersDict
from zerver.lib.user_groups import ( from zerver.lib.user_groups import (
convert_to_user_group_members_dict, convert_to_user_group_members_dict,
get_group_setting_value_for_api, get_group_setting_value_for_api,
get_group_setting_value_for_audit_log_data, get_group_setting_value_for_audit_log_data,
get_recursive_supergroups_union_for_groups,
get_role_based_system_groups_dict, get_role_based_system_groups_dict,
set_defaults_for_group_settings, set_defaults_for_group_settings,
) )
@@ -303,6 +312,17 @@ def bulk_add_members_to_user_groups(
# a single group; but it's easy enough for the implementation to # a single group; but it's easy enough for the implementation to
# support both. # support both.
if len(user_groups) == 0 or len(user_profile_ids) == 0:
return
realm = user_groups[0].realm
supergroups = get_recursive_supergroups_union_for_groups(
[user_group.id for user_group in user_groups]
)
streams = list(
get_metadata_access_streams_via_group_ids([group.id for group in supergroups], realm)
)
old_stream_metadata_user_ids = bulk_can_access_stream_metadata_user_ids(streams)
memberships = [ memberships = [
UserGroupMembership(user_group_id=user_group.id, user_profile_id=user_id) UserGroupMembership(user_group_id=user_group.id, user_profile_id=user_id)
for user_id in user_profile_ids for user_id in user_profile_ids
@@ -312,7 +332,7 @@ def bulk_add_members_to_user_groups(
now = timezone_now() now = timezone_now()
RealmAuditLog.objects.bulk_create( RealmAuditLog.objects.bulk_create(
RealmAuditLog( RealmAuditLog(
realm=user_group.realm, realm=realm,
modified_user_id=user_id, modified_user_id=user_id,
modified_user_group=user_group, modified_user_group=user_group,
event_type=AuditLogEventType.USER_GROUP_DIRECT_USER_MEMBERSHIP_ADDED, event_type=AuditLogEventType.USER_GROUP_DIRECT_USER_MEMBERSHIP_ADDED,
@@ -323,9 +343,37 @@ def bulk_add_members_to_user_groups(
for user_group in user_groups for user_group in user_groups
) )
subscriber_ids_for_streams = get_user_ids_for_streams({stream.id for stream in streams})
new_stream_metadata_user_ids = bulk_can_access_stream_metadata_user_ids(streams)
recent_traffic = get_streams_traffic({stream.id for stream in streams}, realm)
anonymous_group_membership = get_anonymous_group_membership_dict_for_streams(streams)
for user_group in user_groups: for user_group in user_groups:
do_send_user_group_members_update_event("add_members", user_group, user_profile_ids) do_send_user_group_members_update_event("add_members", user_group, user_profile_ids)
for stream in streams:
user_ids_gaining_metadata_access = (
new_stream_metadata_user_ids[stream.id] - old_stream_metadata_user_ids[stream.id]
)
send_stream_creation_event(
realm,
stream,
list(user_ids_gaining_metadata_access),
recent_traffic,
anonymous_group_membership,
)
peer_add_event = dict(
type="subscription",
op="peer_add",
stream_ids=[stream.id],
user_ids=sorted(subscriber_ids_for_streams[stream.id]),
)
send_event_on_commit(
realm,
peer_add_event,
user_ids_gaining_metadata_access,
)
@transaction.atomic(savepoint=False) @transaction.atomic(savepoint=False)
def bulk_remove_members_from_user_groups( def bulk_remove_members_from_user_groups(

View File

@@ -2231,6 +2231,48 @@ class NormalActionsTest(BaseAction):
do_update_user_group_name(api_design, "api-deisgn", acting_user=None) do_update_user_group_name(api_design, "api-deisgn", acting_user=None)
check_user_group_update("events[0]", events[0], {"name"}) check_user_group_update("events[0]", events[0], {"name"})
def do_test_user_group_events_on_stream_metadata_access_change(
self, setting_name: str, stream: Stream, user_group: NamedUserGroup
) -> None:
othello = self.example_user("othello")
hamlet = self.example_user("hamlet")
do_change_stream_group_based_setting(stream, setting_name, user_group, acting_user=othello)
if setting_name in Stream.stream_permission_group_settings_granting_metadata_access:
with self.verify_action(num_events=3) as events:
bulk_add_members_to_user_groups([user_group], [hamlet.id], acting_user=None)
check_user_group_add_members("events[0]", events[0])
check_stream_create("events[1]", events[1])
check_subscription_peer_add("events[2]", events[2])
else:
with self.verify_action() as events:
bulk_add_members_to_user_groups([user_group], [hamlet.id], acting_user=None)
check_user_group_add_members("events[0]", events[0])
# Remove group member for the next test
bulk_remove_members_from_user_groups([user_group], [hamlet.id], acting_user=None)
nobody_group = NamedUserGroup.objects.get(
name=SystemGroups.NOBODY, realm=othello.realm, is_system_group=True
)
do_change_stream_group_based_setting(
stream, setting_name, nobody_group, acting_user=othello
)
def test_user_group_events_on_stream_metadata_access_change(self) -> None:
test_group = check_add_user_group(
self.user_profile.realm,
"test_group",
[self.example_user("othello")],
"Test group",
acting_user=self.example_user("othello"),
)
private_stream = self.make_stream("private_stream", invite_only=True)
for setting_name in Stream.stream_permission_group_settings:
self.do_test_user_group_events_on_stream_metadata_access_change(
setting_name, private_stream, test_group
)
def test_default_stream_groups_events(self) -> None: def test_default_stream_groups_events(self) -> None:
streams = [ streams = [
get_stream(stream_name, self.user_profile.realm) get_stream(stream_name, self.user_profile.realm)

View File

@@ -177,7 +177,7 @@ class StreamSetupTest(ZulipTestCase):
new_user = self.create_simple_new_user(realm, new_user_email) new_user = self.create_simple_new_user(realm, new_user_email)
with self.assert_database_query_count(10): with self.assert_database_query_count(12):
set_up_streams_and_groups_for_new_human_user( set_up_streams_and_groups_for_new_human_user(
user_profile=new_user, user_profile=new_user,
prereg_user=prereg_user, prereg_user=prereg_user,

View File

@@ -555,6 +555,21 @@ class UserGroupTestCase(ZulipTestCase):
with self.assertRaisesRegex(JsonableError, "Invalid system group name."): with self.assertRaisesRegex(JsonableError, "Invalid system group name."):
get_system_user_group_by_name("hamletcharacters", realm.id) get_system_user_group_by_name("hamletcharacters", realm.id)
def test_update_user_group_members_noop_case(self) -> None:
hamlet = self.example_user("hamlet")
test_group = check_add_user_group(
hamlet.realm,
"test_group",
[self.example_user("othello")],
"Test group",
acting_user=self.example_user("othello"),
)
# These functions should not do anything if any of the list
# arguments is empty.
with self.capture_send_event_calls(expected_num_events=0):
bulk_add_members_to_user_groups([], [hamlet.id], acting_user=None)
bulk_add_members_to_user_groups([test_group], [], acting_user=None)
class UserGroupAPITestCase(UserGroupTestCase): class UserGroupAPITestCase(UserGroupTestCase):
def test_user_group_create(self) -> None: def test_user_group_create(self) -> None:
@@ -1734,7 +1749,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
with ( with (
mock.patch("zerver.views.user_groups.notify_for_user_group_subscription_changes"), mock.patch("zerver.views.user_groups.notify_for_user_group_subscription_changes"),
self.assert_database_query_count(14), self.assert_database_query_count(16),
): ):
result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params) result = self.client_post(f"/json/user_groups/{user_group.id}/members", info=params)
self.assert_json_success(result) self.assert_json_success(result)