user_groups: Audit UserGroup supergroup memberships changes.

This is mostly the same as tracking subgroup changes, except that now
modified_user_group is the subgroup.

Signed-off-by: Zixuan James Li <p359101898@gmail.com>
This commit is contained in:
Zixuan James Li
2022-12-11 21:29:10 -05:00
committed by Tim Abbott
parent ad698d597a
commit 3035854dca
4 changed files with 106 additions and 25 deletions

View File

@@ -300,14 +300,28 @@ def add_subgroups_to_user_group(
subgroup_ids = [subgroup.id for subgroup in subgroups] subgroup_ids = [subgroup.id for subgroup in subgroups]
now = timezone_now() now = timezone_now()
RealmAuditLog.objects.create( audit_log_entries = [
realm=user_group.realm, RealmAuditLog(
modified_user_group=user_group, realm=user_group.realm,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED, modified_user_group=user_group,
event_time=now, event_type=RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED,
acting_user=acting_user, event_time=now,
extra_data=orjson.dumps({"subgroup_ids": subgroup_ids}).decode(), acting_user=acting_user,
) extra_data=orjson.dumps({"subgroup_ids": subgroup_ids}).decode(),
)
]
for subgroup_id in subgroup_ids:
audit_log_entries.append(
RealmAuditLog(
realm=user_group.realm,
modified_user_group_id=subgroup_id,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_ADDED,
event_time=now,
acting_user=acting_user,
extra_data=orjson.dumps({"supergroup_ids": [user_group.id]}).decode(),
)
)
RealmAuditLog.objects.bulk_create(audit_log_entries)
do_send_subgroups_update_event("add_subgroups", user_group, subgroup_ids) do_send_subgroups_update_event("add_subgroups", user_group, subgroup_ids)
@@ -320,14 +334,28 @@ def remove_subgroups_from_user_group(
subgroup_ids = [subgroup.id for subgroup in subgroups] subgroup_ids = [subgroup.id for subgroup in subgroups]
now = timezone_now() now = timezone_now()
RealmAuditLog.objects.create( audit_log_entries = [
realm=user_group.realm, RealmAuditLog(
modified_user_group=user_group, realm=user_group.realm,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_REMOVED, modified_user_group=user_group,
event_time=now, event_type=RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_REMOVED,
acting_user=acting_user, event_time=now,
extra_data=orjson.dumps({"subgroup_ids": subgroup_ids}).decode(), acting_user=acting_user,
) extra_data=orjson.dumps({"subgroup_ids": subgroup_ids}).decode(),
)
]
for subgroup_id in subgroup_ids:
audit_log_entries.append(
RealmAuditLog(
realm=user_group.realm,
modified_user_group_id=subgroup_id,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_REMOVED,
event_time=now,
acting_user=acting_user,
extra_data=orjson.dumps({"supergroup_ids": [user_group.id]}).decode(),
)
)
RealmAuditLog.objects.bulk_create(audit_log_entries)
do_send_subgroups_update_event("remove_subgroups", user_group, subgroup_ids) do_send_subgroups_update_event("remove_subgroups", user_group, subgroup_ids)

View File

@@ -352,15 +352,26 @@ def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
subgroup, remaining_groups = system_user_groups_list[1], system_user_groups_list[2:] subgroup, remaining_groups = system_user_groups_list[1], system_user_groups_list[2:]
for supergroup in remaining_groups: for supergroup in remaining_groups:
subgroup_objects.append(GroupGroupMembership(subgroup=subgroup, supergroup=supergroup)) subgroup_objects.append(GroupGroupMembership(subgroup=subgroup, supergroup=supergroup))
realmauditlog_objects.append( now = timezone_now()
RealmAuditLog( realmauditlog_objects.extend(
realm=realm, [
modified_user_group=supergroup, RealmAuditLog(
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED, realm=realm,
event_time=timezone_now(), modified_user_group=supergroup,
acting_user=None, event_type=RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED,
extra_data=orjson.dumps({"subgroup_ids": [subgroup.id]}).decode(), event_time=now,
) acting_user=None,
extra_data=orjson.dumps({"subgroup_ids": [subgroup.id]}).decode(),
),
RealmAuditLog(
realm=realm,
modified_user_group=subgroup,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_ADDED,
event_time=now,
acting_user=None,
extra_data=orjson.dumps({"supergroup_ids": [supergroup.id]}).decode(),
),
]
) )
subgroup = supergroup subgroup = supergroup

View File

@@ -1100,9 +1100,18 @@ class TestRealmAuditLog(ZulipTestCase):
acting_user=None, acting_user=None,
).values_list("modified_user_group_id", "extra_data") ).values_list("modified_user_group_id", "extra_data")
) )
logged_supergroup_entries = sorted(
RealmAuditLog.objects.filter(
realm=realm,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_ADDED,
event_time__gte=now,
acting_user=None,
).values_list("modified_user_group_id", "extra_data")
)
# Excluding nobody_system_group, the rest of the user groups should have # Excluding nobody_system_group, the rest of the user groups should have
# a chain of subgroup memberships in between. # a chain of subgroup memberships in between.
self.assert_length(logged_subgroup_entries, expected_system_user_group_count - 2) self.assert_length(logged_subgroup_entries, expected_system_user_group_count - 2)
self.assert_length(logged_supergroup_entries, expected_system_user_group_count - 2)
for i in range(len(logged_subgroup_entries)): for i in range(len(logged_subgroup_entries)):
# The offset of 1 is due to nobody_system_group being skipped as # The offset of 1 is due to nobody_system_group being skipped as
# the first user group in the list. # the first user group in the list.
@@ -1112,11 +1121,17 @@ class TestRealmAuditLog(ZulipTestCase):
expected_supergroup_id = system_user_group_ids[i + 2] expected_supergroup_id = system_user_group_ids[i + 2]
supergroup_id, subgroup_extra_data = logged_subgroup_entries[i] supergroup_id, subgroup_extra_data = logged_subgroup_entries[i]
subgroup_id, supergroup_extra_data = logged_supergroup_entries[i]
assert subgroup_extra_data is not None assert subgroup_extra_data is not None
assert supergroup_extra_data is not None
self.assertEqual( self.assertEqual(
orjson.loads(subgroup_extra_data)["subgroup_ids"][0], expected_subgroup_id orjson.loads(subgroup_extra_data)["subgroup_ids"][0], expected_subgroup_id
) )
self.assertEqual(
orjson.loads(supergroup_extra_data)["supergroup_ids"][0], expected_supergroup_id
)
self.assertEqual(supergroup_id, expected_supergroup_id) self.assertEqual(supergroup_id, expected_supergroup_id)
self.assertEqual(subgroup_id, expected_subgroup_id)
def test_user_group_creation(self) -> None: def test_user_group_creation(self) -> None:
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")
@@ -1197,6 +1212,19 @@ class TestRealmAuditLog(ZulipTestCase):
orjson.loads(assert_is_not_none(audit_log_entry.extra_data)), orjson.loads(assert_is_not_none(audit_log_entry.extra_data)),
{"subgroup_ids": [subgroup.id for subgroup in subgroups]}, {"subgroup_ids": [subgroup.id for subgroup in subgroups]},
) )
audit_log_entries = RealmAuditLog.objects.filter(
realm=hamlet.realm,
event_time__gte=now,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_ADDED,
).order_by("id")
self.assert_length(audit_log_entries, 3)
for i in range(3):
self.assertEqual(audit_log_entries[i].modified_user_group, subgroups[i])
self.assertEqual(audit_log_entries[i].acting_user, hamlet)
self.assertDictEqual(
orjson.loads(assert_is_not_none(audit_log_entries[i].extra_data)),
{"supergroup_ids": [user_group.id]},
)
remove_subgroups_from_user_group(user_group, subgroups[:2], acting_user=hamlet) remove_subgroups_from_user_group(user_group, subgroups[:2], acting_user=hamlet)
audit_log_entry = RealmAuditLog.objects.get( audit_log_entry = RealmAuditLog.objects.get(
@@ -1210,3 +1238,16 @@ class TestRealmAuditLog(ZulipTestCase):
orjson.loads(assert_is_not_none(audit_log_entry.extra_data)), orjson.loads(assert_is_not_none(audit_log_entry.extra_data)),
{"subgroup_ids": [subgroup.id for subgroup in subgroups[:2]]}, {"subgroup_ids": [subgroup.id for subgroup in subgroups[:2]]},
) )
audit_log_entries = RealmAuditLog.objects.filter(
realm=hamlet.realm,
event_time__gte=now,
event_type=RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_REMOVED,
).order_by("id")
self.assert_length(audit_log_entries, 2)
for i in range(2):
self.assertEqual(audit_log_entries[i].modified_user_group, subgroups[i])
self.assertEqual(audit_log_entries[i].acting_user, hamlet)
self.assertDictEqual(
orjson.loads(assert_is_not_none(audit_log_entries[i].extra_data)),
{"supergroup_ids": [user_group.id]},
)

View File

@@ -1336,6 +1336,7 @@ class SlackImporter(ZulipTestCase):
RealmAuditLog.REALM_CREATED, RealmAuditLog.REALM_CREATED,
RealmAuditLog.USER_GROUP_CREATED, RealmAuditLog.USER_GROUP_CREATED,
RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED, RealmAuditLog.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED,
RealmAuditLog.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_ADDED,
}, },
) )