mirror of
https://github.com/zulip/zulip.git
synced 2025-11-13 02:17:19 +00:00
For get and filter queries of NamedUserGroup, realm_for_sharding field is used instead of realm field, as directly using realm_for_sharding field on NamedUserGroup makes the query faster than using realm present on the base UserGroup table.
682 lines
24 KiB
Python
682 lines
24 KiB
Python
from collections.abc import Mapping, Sequence
|
|
from datetime import datetime
|
|
from typing import TypedDict
|
|
|
|
import django.db.utils
|
|
from django.db import transaction
|
|
from django.utils.timezone import now as timezone_now
|
|
from django.utils.translation import gettext as _
|
|
|
|
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,
|
|
send_stream_deletion_event,
|
|
)
|
|
from zerver.lib.timestamp import datetime_to_timestamp
|
|
from zerver.lib.types import UserGroupMembersData, UserGroupMembersDict
|
|
from zerver.lib.user_groups import (
|
|
convert_to_user_group_members_dict,
|
|
get_group_setting_value_for_api,
|
|
get_group_setting_value_for_audit_log_data,
|
|
get_recursive_supergroups_union_for_groups,
|
|
get_role_based_system_groups_dict,
|
|
set_defaults_for_group_settings,
|
|
)
|
|
from zerver.models import (
|
|
GroupGroupMembership,
|
|
NamedUserGroup,
|
|
Realm,
|
|
RealmAuditLog,
|
|
UserGroup,
|
|
UserGroupMembership,
|
|
UserProfile,
|
|
)
|
|
from zerver.models.groups import SystemGroups
|
|
from zerver.models.realm_audit_logs import AuditLogEventType
|
|
from zerver.models.users import active_user_ids
|
|
from zerver.tornado.django_api import send_event_on_commit
|
|
|
|
|
|
class MemberGroupUserDict(TypedDict):
|
|
id: int
|
|
role: int
|
|
date_joined: datetime
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def create_user_group_in_database(
|
|
name: str,
|
|
members: list[UserProfile],
|
|
realm: Realm,
|
|
*,
|
|
acting_user: UserProfile | None,
|
|
description: str = "",
|
|
group_settings_map: Mapping[str, UserGroup] = {},
|
|
is_system_group: bool = False,
|
|
) -> NamedUserGroup:
|
|
user_group = NamedUserGroup(
|
|
name=name,
|
|
realm=realm,
|
|
description=description,
|
|
is_system_group=is_system_group,
|
|
realm_for_sharding=realm,
|
|
creator=acting_user,
|
|
)
|
|
|
|
for setting_name, setting_value in group_settings_map.items():
|
|
setattr(user_group, setting_name, setting_value)
|
|
|
|
system_groups_name_dict = get_role_based_system_groups_dict(realm)
|
|
user_group = set_defaults_for_group_settings(
|
|
user_group, group_settings_map, system_groups_name_dict
|
|
)
|
|
user_group.save()
|
|
|
|
UserGroupMembership.objects.bulk_create(
|
|
UserGroupMembership(user_profile=member, user_group=user_group) for member in members
|
|
)
|
|
|
|
creation_time = timezone_now()
|
|
audit_log_entries = [
|
|
RealmAuditLog(
|
|
realm=realm,
|
|
acting_user=acting_user,
|
|
event_type=AuditLogEventType.USER_GROUP_CREATED,
|
|
event_time=creation_time,
|
|
modified_user_group=user_group,
|
|
),
|
|
] + [
|
|
RealmAuditLog(
|
|
realm=realm,
|
|
acting_user=acting_user,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_USER_MEMBERSHIP_ADDED,
|
|
event_time=creation_time,
|
|
modified_user=member,
|
|
modified_user_group=user_group,
|
|
)
|
|
for member in members
|
|
]
|
|
RealmAuditLog.objects.bulk_create(audit_log_entries)
|
|
return user_group
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def update_users_in_full_members_system_group(
|
|
realm: Realm, affected_user_ids: Sequence[int] = [], *, acting_user: UserProfile | None
|
|
) -> None:
|
|
full_members_system_group = NamedUserGroup.objects.get(
|
|
realm_for_sharding=realm, name=SystemGroups.FULL_MEMBERS, is_system_group=True
|
|
)
|
|
members_system_group = NamedUserGroup.objects.get(
|
|
realm_for_sharding=realm, name=SystemGroups.MEMBERS, is_system_group=True
|
|
)
|
|
|
|
full_member_group_users: list[MemberGroupUserDict] = list()
|
|
member_group_users: list[MemberGroupUserDict] = list()
|
|
|
|
if affected_user_ids:
|
|
full_member_group_users = list(
|
|
full_members_system_group.direct_members.filter(id__in=affected_user_ids).values(
|
|
"id", "role", "date_joined"
|
|
)
|
|
)
|
|
member_group_users = list(
|
|
members_system_group.direct_members.filter(id__in=affected_user_ids).values(
|
|
"id", "role", "date_joined"
|
|
)
|
|
)
|
|
else:
|
|
full_member_group_users = list(
|
|
full_members_system_group.direct_members.all().values("id", "role", "date_joined")
|
|
)
|
|
member_group_users = list(
|
|
members_system_group.direct_members.all().values("id", "role", "date_joined")
|
|
)
|
|
|
|
def is_provisional_member(user: MemberGroupUserDict) -> bool:
|
|
diff = (timezone_now() - user["date_joined"]).days
|
|
if diff < realm.waiting_period_threshold:
|
|
return True
|
|
return False
|
|
|
|
old_full_members = [
|
|
user
|
|
for user in full_member_group_users
|
|
if is_provisional_member(user) or user["role"] != UserProfile.ROLE_MEMBER
|
|
]
|
|
|
|
full_member_group_user_ids = [user["id"] for user in full_member_group_users]
|
|
members_excluding_full_members = [
|
|
user for user in member_group_users if user["id"] not in full_member_group_user_ids
|
|
]
|
|
|
|
new_full_members = [
|
|
user for user in members_excluding_full_members if not is_provisional_member(user)
|
|
]
|
|
|
|
old_full_member_ids = [user["id"] for user in old_full_members]
|
|
new_full_member_ids = [user["id"] for user in new_full_members]
|
|
|
|
if len(old_full_members) > 0:
|
|
bulk_remove_members_from_user_groups(
|
|
[full_members_system_group], old_full_member_ids, acting_user=acting_user
|
|
)
|
|
|
|
if len(new_full_members) > 0:
|
|
bulk_add_members_to_user_groups(
|
|
[full_members_system_group], new_full_member_ids, acting_user=acting_user
|
|
)
|
|
|
|
|
|
def promote_new_full_members() -> None:
|
|
for realm in Realm.objects.filter(deactivated=False).exclude(waiting_period_threshold=0):
|
|
update_users_in_full_members_system_group(realm, acting_user=None)
|
|
|
|
|
|
def do_send_create_user_group_event(
|
|
user_group: NamedUserGroup,
|
|
member_ids: list[int],
|
|
direct_subgroup_ids: Sequence[int] = [],
|
|
*,
|
|
for_reactivation: bool = False,
|
|
) -> None:
|
|
creator_id = user_group.creator_id
|
|
assert user_group.date_created is not None
|
|
date_created = datetime_to_timestamp(user_group.date_created)
|
|
|
|
setting_values = {}
|
|
for setting_name in NamedUserGroup.GROUP_PERMISSION_SETTINGS:
|
|
setting_values[setting_name] = convert_to_user_group_members_dict(
|
|
get_group_setting_value_for_api(getattr(user_group, setting_name))
|
|
)
|
|
|
|
event = dict(
|
|
type="user_group",
|
|
op="add",
|
|
group=dict(
|
|
name=user_group.name,
|
|
creator_id=creator_id,
|
|
date_created=date_created,
|
|
members=member_ids,
|
|
description=user_group.description,
|
|
id=user_group.id,
|
|
is_system_group=user_group.is_system_group,
|
|
direct_subgroup_ids=direct_subgroup_ids,
|
|
**setting_values,
|
|
deactivated=False,
|
|
),
|
|
for_reactivation=for_reactivation,
|
|
)
|
|
send_event_on_commit(user_group.realm, event, active_user_ids(user_group.realm_id))
|
|
|
|
|
|
def check_add_user_group(
|
|
realm: Realm,
|
|
name: str,
|
|
initial_members: list[UserProfile],
|
|
description: str = "",
|
|
group_settings_map: Mapping[str, UserGroup] = {},
|
|
*,
|
|
acting_user: UserProfile | None,
|
|
) -> NamedUserGroup:
|
|
try:
|
|
user_group = create_user_group_in_database(
|
|
name,
|
|
initial_members,
|
|
realm,
|
|
description=description,
|
|
group_settings_map=group_settings_map,
|
|
acting_user=acting_user,
|
|
)
|
|
do_send_create_user_group_event(user_group, [member.id for member in initial_members])
|
|
return user_group
|
|
except django.db.utils.IntegrityError:
|
|
raise JsonableError(_("User group '{group_name}' already exists.").format(group_name=name))
|
|
|
|
|
|
def do_send_user_group_update_event(
|
|
user_group: NamedUserGroup, data: dict[str, str | int | UserGroupMembersDict]
|
|
) -> None:
|
|
event = dict(type="user_group", op="update", group_id=user_group.id, data=data)
|
|
if "name" in data:
|
|
# This field will be popped eventually before sending the event
|
|
# to client, but is needed to make sure we do not send the
|
|
# name update event for deactivated groups to client with
|
|
# 'include_deactivated_groups' client capability set to false.
|
|
event["deactivated"] = user_group.deactivated
|
|
send_event_on_commit(user_group.realm, event, active_user_ids(user_group.realm_id))
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def do_update_user_group_name(
|
|
user_group: NamedUserGroup, name: str, *, acting_user: UserProfile | None
|
|
) -> None:
|
|
try:
|
|
old_value = user_group.name
|
|
user_group.name = name
|
|
user_group.save(update_fields=["name"])
|
|
RealmAuditLog.objects.create(
|
|
realm=user_group.realm,
|
|
modified_user_group=user_group,
|
|
event_type=AuditLogEventType.USER_GROUP_NAME_CHANGED,
|
|
event_time=timezone_now(),
|
|
acting_user=acting_user,
|
|
extra_data={
|
|
RealmAuditLog.OLD_VALUE: old_value,
|
|
RealmAuditLog.NEW_VALUE: name,
|
|
},
|
|
)
|
|
except django.db.utils.IntegrityError:
|
|
raise JsonableError(_("User group '{group_name}' already exists.").format(group_name=name))
|
|
do_send_user_group_update_event(user_group, dict(name=name))
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def do_update_user_group_description(
|
|
user_group: NamedUserGroup, description: str, *, acting_user: UserProfile | None
|
|
) -> None:
|
|
old_value = user_group.description
|
|
user_group.description = description
|
|
user_group.save(update_fields=["description"])
|
|
RealmAuditLog.objects.create(
|
|
realm=user_group.realm,
|
|
modified_user_group=user_group,
|
|
event_type=AuditLogEventType.USER_GROUP_DESCRIPTION_CHANGED,
|
|
event_time=timezone_now(),
|
|
acting_user=acting_user,
|
|
extra_data={
|
|
RealmAuditLog.OLD_VALUE: old_value,
|
|
RealmAuditLog.NEW_VALUE: description,
|
|
},
|
|
)
|
|
do_send_user_group_update_event(user_group, dict(description=description))
|
|
|
|
|
|
def do_send_user_group_members_update_event(
|
|
event_name: str, user_group: NamedUserGroup, user_ids: list[int]
|
|
) -> None:
|
|
event = dict(type="user_group", op=event_name, group_id=user_group.id, user_ids=user_ids)
|
|
send_event_on_commit(user_group.realm, event, active_user_ids(user_group.realm_id))
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def bulk_add_members_to_user_groups(
|
|
user_groups: list[NamedUserGroup],
|
|
user_profile_ids: list[int],
|
|
*,
|
|
acting_user: UserProfile | None,
|
|
) -> None:
|
|
# All intended callers of this function involve a single user
|
|
# being added to one or more groups, or many users being added to
|
|
# a single group; but it's easy enough for the implementation to
|
|
# 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 = [
|
|
UserGroupMembership(user_group_id=user_group.id, user_profile_id=user_id)
|
|
for user_id in user_profile_ids
|
|
for user_group in user_groups
|
|
]
|
|
UserGroupMembership.objects.bulk_create(memberships)
|
|
now = timezone_now()
|
|
RealmAuditLog.objects.bulk_create(
|
|
RealmAuditLog(
|
|
realm=realm,
|
|
modified_user_id=user_id,
|
|
modified_user_group=user_group,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_USER_MEMBERSHIP_ADDED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
)
|
|
for user_id in user_profile_ids
|
|
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:
|
|
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)
|
|
def bulk_remove_members_from_user_groups(
|
|
user_groups: list[NamedUserGroup],
|
|
user_profile_ids: list[int],
|
|
*,
|
|
acting_user: UserProfile | None,
|
|
) -> None:
|
|
# All intended callers of this function involve a single user
|
|
# being added to one or more groups, or many users being added to
|
|
# a single group; but it's easy enough for the implementation to
|
|
# 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)
|
|
|
|
UserGroupMembership.objects.filter(
|
|
user_group__in=user_groups, user_profile_id__in=user_profile_ids
|
|
).delete()
|
|
now = timezone_now()
|
|
RealmAuditLog.objects.bulk_create(
|
|
RealmAuditLog(
|
|
realm=user_group.realm,
|
|
modified_user_id=user_id,
|
|
modified_user_group=user_group,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_USER_MEMBERSHIP_REMOVED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
)
|
|
for user_id in user_profile_ids
|
|
for user_group in user_groups
|
|
)
|
|
|
|
for user_group in user_groups:
|
|
do_send_user_group_members_update_event("remove_members", user_group, user_profile_ids)
|
|
|
|
new_stream_metadata_user_ids = bulk_can_access_stream_metadata_user_ids(streams)
|
|
for stream in streams:
|
|
user_ids_losing_metadata_access = (
|
|
old_stream_metadata_user_ids[stream.id] - new_stream_metadata_user_ids[stream.id]
|
|
)
|
|
send_stream_deletion_event(
|
|
realm,
|
|
list(user_ids_losing_metadata_access),
|
|
[stream],
|
|
)
|
|
|
|
|
|
def do_send_subgroups_update_event(
|
|
event_name: str, user_group: NamedUserGroup, subgroup_ids: list[int]
|
|
) -> None:
|
|
event = dict(
|
|
type="user_group", op=event_name, group_id=user_group.id, direct_subgroup_ids=subgroup_ids
|
|
)
|
|
send_event_on_commit(user_group.realm, event, active_user_ids(user_group.realm_id))
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def add_subgroups_to_user_group(
|
|
user_group: NamedUserGroup,
|
|
subgroups: list[NamedUserGroup],
|
|
*,
|
|
acting_user: UserProfile | None,
|
|
) -> None:
|
|
if len(subgroups) == 0:
|
|
return
|
|
|
|
realm = user_group.realm
|
|
supergroups = get_recursive_supergroups_union_for_groups([user_group.id])
|
|
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)
|
|
|
|
group_memberships = [
|
|
GroupGroupMembership(supergroup=user_group, subgroup=subgroup) for subgroup in subgroups
|
|
]
|
|
GroupGroupMembership.objects.bulk_create(group_memberships)
|
|
|
|
subgroup_ids = [subgroup.id for subgroup in subgroups]
|
|
now = timezone_now()
|
|
audit_log_entries = [
|
|
RealmAuditLog(
|
|
realm=user_group.realm,
|
|
modified_user_group=user_group,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_ADDED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
extra_data={"subgroup_ids": subgroup_ids},
|
|
),
|
|
*(
|
|
RealmAuditLog(
|
|
realm=user_group.realm,
|
|
modified_user_group_id=subgroup_id,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_ADDED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
extra_data={"supergroup_ids": [user_group.id]},
|
|
)
|
|
for subgroup_id in subgroup_ids
|
|
),
|
|
]
|
|
RealmAuditLog.objects.bulk_create(audit_log_entries)
|
|
|
|
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)
|
|
|
|
do_send_subgroups_update_event("add_subgroups", user_group, subgroup_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)
|
|
def remove_subgroups_from_user_group(
|
|
user_group: NamedUserGroup,
|
|
subgroups: list[NamedUserGroup],
|
|
*,
|
|
acting_user: UserProfile | None,
|
|
) -> None:
|
|
if len(subgroups) == 0:
|
|
return
|
|
|
|
realm = user_group.realm
|
|
supergroups = get_recursive_supergroups_union_for_groups([user_group.id])
|
|
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)
|
|
|
|
GroupGroupMembership.objects.filter(supergroup=user_group, subgroup__in=subgroups).delete()
|
|
|
|
subgroup_ids = [subgroup.id for subgroup in subgroups]
|
|
now = timezone_now()
|
|
audit_log_entries = [
|
|
RealmAuditLog(
|
|
realm=user_group.realm,
|
|
modified_user_group=user_group,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_SUBGROUP_MEMBERSHIP_REMOVED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
extra_data={"subgroup_ids": subgroup_ids},
|
|
),
|
|
*(
|
|
RealmAuditLog(
|
|
realm=user_group.realm,
|
|
modified_user_group_id=subgroup_id,
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_SUPERGROUP_MEMBERSHIP_REMOVED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
extra_data={"supergroup_ids": [user_group.id]},
|
|
)
|
|
for subgroup_id in subgroup_ids
|
|
),
|
|
]
|
|
RealmAuditLog.objects.bulk_create(audit_log_entries)
|
|
|
|
do_send_subgroups_update_event("remove_subgroups", user_group, subgroup_ids)
|
|
|
|
new_stream_metadata_user_ids = bulk_can_access_stream_metadata_user_ids(streams)
|
|
for stream in streams:
|
|
user_ids_losing_metadata_access = (
|
|
old_stream_metadata_user_ids[stream.id] - new_stream_metadata_user_ids[stream.id]
|
|
)
|
|
send_stream_deletion_event(
|
|
realm,
|
|
list(user_ids_losing_metadata_access),
|
|
[stream],
|
|
)
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def do_deactivate_user_group(
|
|
user_group: NamedUserGroup, *, acting_user: UserProfile | None
|
|
) -> None:
|
|
user_group.deactivated = True
|
|
user_group.save(update_fields=["deactivated"])
|
|
|
|
now = timezone_now()
|
|
RealmAuditLog.objects.create(
|
|
realm=user_group.realm,
|
|
modified_user_group_id=user_group.id,
|
|
event_type=AuditLogEventType.USER_GROUP_DEACTIVATED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
)
|
|
|
|
do_send_user_group_update_event(user_group, dict(deactivated=True))
|
|
|
|
event = dict(type="user_group", op="remove", group_id=user_group.id)
|
|
send_event_on_commit(user_group.realm, event, active_user_ids(user_group.realm_id))
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def do_reactivate_user_group(
|
|
user_group: NamedUserGroup, *, acting_user: UserProfile | None
|
|
) -> None:
|
|
user_group.deactivated = False
|
|
user_group.save(update_fields=["deactivated"])
|
|
|
|
now = timezone_now()
|
|
RealmAuditLog.objects.create(
|
|
realm=user_group.realm,
|
|
modified_user_group_id=user_group.id,
|
|
event_type=AuditLogEventType.USER_GROUP_REACTIVATED,
|
|
event_time=now,
|
|
acting_user=acting_user,
|
|
)
|
|
|
|
do_send_user_group_update_event(user_group, dict(deactivated=False))
|
|
|
|
direct_member_ids = list(user_group.direct_members.all().values_list("id", flat=True))
|
|
direct_subgroup_ids = list(user_group.direct_subgroups.all().values_list("id", flat=True))
|
|
do_send_create_user_group_event(
|
|
user_group, direct_member_ids, direct_subgroup_ids, for_reactivation=True
|
|
)
|
|
|
|
|
|
@transaction.atomic(savepoint=False)
|
|
def do_change_user_group_permission_setting(
|
|
user_group: NamedUserGroup,
|
|
setting_name: str,
|
|
setting_value_group: UserGroup,
|
|
*,
|
|
old_setting_api_value: int | UserGroupMembersData | None = None,
|
|
acting_user: UserProfile | None,
|
|
) -> None:
|
|
old_value = getattr(user_group, setting_name)
|
|
setattr(user_group, setting_name, setting_value_group)
|
|
user_group.save()
|
|
|
|
if old_setting_api_value is None:
|
|
# Most production callers will have computed this as part of
|
|
# verifying whether there's an actual change to make, but it
|
|
# feels quite clumsy to have to pass it from unit tests, so we
|
|
# compute it here if not provided by the caller.
|
|
old_setting_api_value = get_group_setting_value_for_api(old_value)
|
|
new_setting_api_value = get_group_setting_value_for_api(setting_value_group)
|
|
|
|
if not hasattr(old_value, "named_user_group") and hasattr(
|
|
setting_value_group, "named_user_group"
|
|
):
|
|
# We delete the UserGroup which the setting was set to
|
|
# previously if it does not have any linked NamedUserGroup
|
|
# object, as it is not used anywhere else. A new UserGroup
|
|
# object would be created if the setting is later set to
|
|
# a combination of users and groups.
|
|
old_value.delete()
|
|
|
|
RealmAuditLog.objects.create(
|
|
realm=user_group.realm,
|
|
acting_user=acting_user,
|
|
event_type=AuditLogEventType.USER_GROUP_GROUP_BASED_SETTING_CHANGED,
|
|
event_time=timezone_now(),
|
|
modified_user_group=user_group,
|
|
extra_data={
|
|
RealmAuditLog.OLD_VALUE: get_group_setting_value_for_audit_log_data(
|
|
old_setting_api_value
|
|
),
|
|
RealmAuditLog.NEW_VALUE: get_group_setting_value_for_audit_log_data(
|
|
new_setting_api_value
|
|
),
|
|
"property": setting_name,
|
|
},
|
|
)
|
|
|
|
event_data_dict: dict[str, str | int | UserGroupMembersDict] = {
|
|
setting_name: convert_to_user_group_members_dict(new_setting_api_value)
|
|
}
|
|
do_send_user_group_update_event(user_group, event_data_dict)
|