diff --git a/analytics/management/commands/populate_analytics_db.py b/analytics/management/commands/populate_analytics_db.py index 7167196280..0826217bce 100644 --- a/analytics/management/commands/populate_analytics_db.py +++ b/analytics/management/commands/populate_analytics_db.py @@ -32,10 +32,10 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserProfile, ) +from zerver.models.groups import SystemGroups class Command(BaseCommand): diff --git a/analytics/tests/test_counts.py b/analytics/tests/test_counts.py index b3af4ac8d5..02765c7b00 100644 --- a/analytics/tests/test_counts.py +++ b/analytics/tests/test_counts.py @@ -74,12 +74,12 @@ from zerver.models import ( RealmAuditLog, Recipient, Stream, - SystemGroups, UserActivityInterval, UserGroup, UserProfile, get_client, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_user, is_cross_realm_bot_email from zilencer.models import ( RemoteInstallationCount, diff --git a/zerver/actions/create_user.py b/zerver/actions/create_user.py index f52586ffab..af7dc0b211 100644 --- a/zerver/actions/create_user.py +++ b/zerver/actions/create_user.py @@ -44,12 +44,12 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserGroupMembership, UserMessage, UserProfile, ) +from zerver.models.groups import SystemGroups from zerver.models.users import active_user_ids, bot_owner_user_ids, get_system_bot from zerver.tornado.django_api import send_event_on_commit diff --git a/zerver/actions/message_send.py b/zerver/actions/message_send.py index 11ded34975..5c62a83c35 100644 --- a/zerver/actions/message_send.py +++ b/zerver/actions/message_send.py @@ -98,7 +98,6 @@ from zerver.models import ( Realm, Recipient, Stream, - SystemGroups, UserMessage, UserPresence, UserProfile, @@ -109,6 +108,7 @@ from zerver.models import ( get_stream_by_id_in_realm, query_for_ids, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_system_bot, get_user_by_delivery_email, is_cross_realm_bot_email from zerver.tornado.django_api import send_event diff --git a/zerver/actions/realm_settings.py b/zerver/actions/realm_settings.py index 6bf36fba0a..4817bfae14 100644 --- a/zerver/actions/realm_settings.py +++ b/zerver/actions/realm_settings.py @@ -31,11 +31,11 @@ from zerver.models import ( ScheduledEmail, Stream, Subscription, - SystemGroups, UserGroup, UserProfile, get_realm, ) +from zerver.models.groups import SystemGroups from zerver.models.users import active_user_ids from zerver.tornado.django_api import send_event, send_event_on_commit diff --git a/zerver/actions/streams.py b/zerver/actions/streams.py index a1bcbeb481..53251558eb 100644 --- a/zerver/actions/streams.py +++ b/zerver/actions/streams.py @@ -64,10 +64,10 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserProfile, ) +from zerver.models.groups import SystemGroups from zerver.models.users import active_non_guest_user_ids, active_user_ids, get_system_bot from zerver.tornado.django_api import send_event, send_event_on_commit diff --git a/zerver/actions/user_groups.py b/zerver/actions/user_groups.py index aa88270372..6194e76959 100644 --- a/zerver/actions/user_groups.py +++ b/zerver/actions/user_groups.py @@ -15,11 +15,11 @@ from zerver.models import ( GroupGroupMembership, Realm, RealmAuditLog, - SystemGroups, UserGroup, UserGroupMembership, UserProfile, ) +from zerver.models.groups import SystemGroups from zerver.models.users import active_user_ids from zerver.tornado.django_api import send_event, send_event_on_commit diff --git a/zerver/lib/bulk_create.py b/zerver/lib/bulk_create.py index 4b66fac499..8232d1b69e 100644 --- a/zerver/lib/bulk_create.py +++ b/zerver/lib/bulk_create.py @@ -13,11 +13,11 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserGroupMembership, UserProfile, ) +from zerver.models.groups import SystemGroups def bulk_create_users( diff --git a/zerver/lib/import_realm.py b/zerver/lib/import_realm.py index 8677e73a35..c84299ae20 100644 --- a/zerver/lib/import_realm.py +++ b/zerver/lib/import_realm.py @@ -67,7 +67,6 @@ from zerver.models import ( Service, Stream, Subscription, - SystemGroups, UserActivity, UserActivityInterval, UserGroup, @@ -80,6 +79,7 @@ from zerver.models import ( get_huddle_hash, get_realm, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_system_bot, get_user_profile_by_id realm_tables = [ diff --git a/zerver/lib/streams.py b/zerver/lib/streams.py index 7036f08a46..6baf271d29 100644 --- a/zerver/lib/streams.py +++ b/zerver/lib/streams.py @@ -28,7 +28,6 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserProfile, bulk_get_streams, @@ -36,6 +35,7 @@ from zerver.models import ( get_stream, get_stream_by_id_in_realm, ) +from zerver.models.groups import SystemGroups from zerver.models.users import active_non_guest_user_ids, active_user_ids, is_cross_realm_bot_email from zerver.tornado.django_api import send_event diff --git a/zerver/lib/test_classes.py b/zerver/lib/test_classes.py index 217ac8919c..7399a6db11 100644 --- a/zerver/lib/test_classes.py +++ b/zerver/lib/test_classes.py @@ -102,7 +102,6 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserGroupMembership, UserMessage, @@ -113,6 +112,7 @@ from zerver.models import ( get_realm_stream, get_stream, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_system_bot, get_user, get_user_by_delivery_email from zerver.openapi.openapi import validate_against_openapi_schema, validate_request from zerver.tornado.event_queue import clear_client_event_queues_for_testing diff --git a/zerver/lib/user_groups.py b/zerver/lib/user_groups.py index 072aaec09a..b27f0194ad 100644 --- a/zerver/lib/user_groups.py +++ b/zerver/lib/user_groups.py @@ -16,11 +16,11 @@ from zerver.models import ( Realm, RealmAuditLog, Stream, - SystemGroups, UserGroup, UserGroupMembership, UserProfile, ) +from zerver.models.groups import SystemGroups class UserGroupDict(TypedDict): diff --git a/zerver/lib/users.py b/zerver/lib/users.py index 818f964921..6a26218300 100644 --- a/zerver/lib/users.py +++ b/zerver/lib/users.py @@ -34,11 +34,11 @@ from zerver.models import ( Recipient, Service, Subscription, - SystemGroups, UserMessage, UserProfile, get_fake_email_domain, ) +from zerver.models.groups import SystemGroups from zerver.models.users import ( active_non_guest_user_ids, active_user_ids, diff --git a/zerver/models/__init__.py b/zerver/models/__init__.py index 0003ea9514..3d6497c230 100644 --- a/zerver/models/__init__.py +++ b/zerver/models/__init__.py @@ -47,7 +47,6 @@ from django.db.models.sql.compiler import SQLCompiler from django.utils.timezone import now as timezone_now from django.utils.translation import gettext as _ from django.utils.translation import gettext_lazy -from django_cte import CTEManager from django_stubs_ext import StrPromise, ValuesQuerySet from typing_extensions import override @@ -101,6 +100,10 @@ from zerver.lib.validator import ( validate_select_field, ) from zerver.models.constants import MAX_LANGUAGE_ID_LENGTH, MAX_TOPIC_NAME_LENGTH +from zerver.models.groups import GroupGroupMembership as GroupGroupMembership +from zerver.models.groups import SystemGroups +from zerver.models.groups import UserGroup as UserGroup +from zerver.models.groups import UserGroupMembership as UserGroupMembership from zerver.models.users import RealmUserDefault as RealmUserDefault from zerver.models.users import UserBaseSettings as UserBaseSettings from zerver.models.users import UserProfile as UserProfile @@ -245,17 +248,6 @@ def clear_supported_auth_backends_cache() -> None: supported_backends = None -class SystemGroups: - FULL_MEMBERS = "role:fullmembers" - EVERYONE_ON_INTERNET = "role:internet" - OWNERS = "role:owners" - ADMINISTRATORS = "role:administrators" - MODERATORS = "role:moderators" - MEMBERS = "role:members" - EVERYONE = "role:everyone" - NOBODY = "role:nobody" - - class RealmAuthenticationMethod(models.Model): """ Tracks which authentication backends are enabled for a realm. @@ -1563,91 +1555,6 @@ class Recipient(models.Model): return self._type_names[self.type] -class UserGroup(models.Model): # type: ignore[django-manager-missing] # django-stubs cannot resolve the custom CTEManager yet https://github.com/typeddjango/django-stubs/issues/1023 - MAX_NAME_LENGTH = 100 - INVALID_NAME_PREFIXES = ["@", "role:", "user:", "stream:", "channel:"] - - objects: CTEManager = CTEManager() - name = models.CharField(max_length=MAX_NAME_LENGTH) - direct_members = models.ManyToManyField( - UserProfile, through="zerver.UserGroupMembership", related_name="direct_groups" - ) - direct_subgroups = models.ManyToManyField( - "self", - symmetrical=False, - through="zerver.GroupGroupMembership", - through_fields=("supergroup", "subgroup"), - related_name="direct_supergroups", - ) - realm = models.ForeignKey(Realm, on_delete=CASCADE) - description = models.TextField(default="") - is_system_group = models.BooleanField(default=False) - - can_mention_group = models.ForeignKey("self", on_delete=models.RESTRICT) - - # We do not have "Full members" and "Everyone on the internet" - # group here since there isn't a separate role value for full - # members and spectators. - SYSTEM_USER_GROUP_ROLE_MAP = { - UserProfile.ROLE_REALM_OWNER: { - "name": SystemGroups.OWNERS, - "description": "Owners of this organization", - }, - UserProfile.ROLE_REALM_ADMINISTRATOR: { - "name": SystemGroups.ADMINISTRATORS, - "description": "Administrators of this organization, including owners", - }, - UserProfile.ROLE_MODERATOR: { - "name": SystemGroups.MODERATORS, - "description": "Moderators of this organization, including administrators", - }, - UserProfile.ROLE_MEMBER: { - "name": SystemGroups.MEMBERS, - "description": "Members of this organization, not including guests", - }, - UserProfile.ROLE_GUEST: { - "name": SystemGroups.EVERYONE, - "description": "Everyone in this organization, including guests", - }, - } - - GROUP_PERMISSION_SETTINGS = { - "can_mention_group": GroupPermissionSetting( - require_system_group=False, - allow_internet_group=False, - allow_owners_group=False, - allow_nobody_group=True, - allow_everyone_group=True, - default_group_name=SystemGroups.EVERYONE, - default_for_system_groups=SystemGroups.NOBODY, - id_field_name="can_mention_group_id", - ), - } - - class Meta: - unique_together = (("realm", "name"),) - - -class UserGroupMembership(models.Model): - user_group = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") - user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE, related_name="+") - - class Meta: - unique_together = (("user_group", "user_profile"),) - - -class GroupGroupMembership(models.Model): - supergroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") - subgroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") - - class Meta: - constraints = [ - models.UniqueConstraint( - fields=["supergroup", "subgroup"], name="zerver_groupgroupmembership_uniq" - ) - ] - - class PreregistrationRealm(models.Model): """Data on a partially created realm entered by a user who has completed the "new organization" form. Used to transfer the user's diff --git a/zerver/models/groups.py b/zerver/models/groups.py new file mode 100644 index 0000000000..eeaf0759db --- /dev/null +++ b/zerver/models/groups.py @@ -0,0 +1,102 @@ +from django.db import models +from django.db.models import CASCADE +from django_cte import CTEManager + +from zerver.lib.types import GroupPermissionSetting +from zerver.models.users import UserProfile + + +class SystemGroups: + FULL_MEMBERS = "role:fullmembers" + EVERYONE_ON_INTERNET = "role:internet" + OWNERS = "role:owners" + ADMINISTRATORS = "role:administrators" + MODERATORS = "role:moderators" + MEMBERS = "role:members" + EVERYONE = "role:everyone" + NOBODY = "role:nobody" + + +class UserGroup(models.Model): # type: ignore[django-manager-missing] # django-stubs cannot resolve the custom CTEManager yet https://github.com/typeddjango/django-stubs/issues/1023 + MAX_NAME_LENGTH = 100 + INVALID_NAME_PREFIXES = ["@", "role:", "user:", "stream:", "channel:"] + + objects: CTEManager = CTEManager() + name = models.CharField(max_length=MAX_NAME_LENGTH) + direct_members = models.ManyToManyField( + UserProfile, through="zerver.UserGroupMembership", related_name="direct_groups" + ) + direct_subgroups = models.ManyToManyField( + "self", + symmetrical=False, + through="zerver.GroupGroupMembership", + through_fields=("supergroup", "subgroup"), + related_name="direct_supergroups", + ) + realm = models.ForeignKey("zerver.Realm", on_delete=CASCADE) + description = models.TextField(default="") + is_system_group = models.BooleanField(default=False) + + can_mention_group = models.ForeignKey("self", on_delete=models.RESTRICT) + + # We do not have "Full members" and "Everyone on the internet" + # group here since there isn't a separate role value for full + # members and spectators. + SYSTEM_USER_GROUP_ROLE_MAP = { + UserProfile.ROLE_REALM_OWNER: { + "name": SystemGroups.OWNERS, + "description": "Owners of this organization", + }, + UserProfile.ROLE_REALM_ADMINISTRATOR: { + "name": SystemGroups.ADMINISTRATORS, + "description": "Administrators of this organization, including owners", + }, + UserProfile.ROLE_MODERATOR: { + "name": SystemGroups.MODERATORS, + "description": "Moderators of this organization, including administrators", + }, + UserProfile.ROLE_MEMBER: { + "name": SystemGroups.MEMBERS, + "description": "Members of this organization, not including guests", + }, + UserProfile.ROLE_GUEST: { + "name": SystemGroups.EVERYONE, + "description": "Everyone in this organization, including guests", + }, + } + + GROUP_PERMISSION_SETTINGS = { + "can_mention_group": GroupPermissionSetting( + require_system_group=False, + allow_internet_group=False, + allow_owners_group=False, + allow_nobody_group=True, + allow_everyone_group=True, + default_group_name=SystemGroups.EVERYONE, + default_for_system_groups=SystemGroups.NOBODY, + id_field_name="can_mention_group_id", + ), + } + + class Meta: + unique_together = (("realm", "name"),) + + +class UserGroupMembership(models.Model): + user_group = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE, related_name="+") + + class Meta: + unique_together = (("user_group", "user_profile"),) + + +class GroupGroupMembership(models.Model): + supergroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") + subgroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") + + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["supergroup", "subgroup"], name="zerver_groupgroupmembership_uniq" + ) + ] diff --git a/zerver/tests/test_audit_log.py b/zerver/tests/test_audit_log.py index b4cf5bd91b..52a5217a75 100644 --- a/zerver/tests/test_audit_log.py +++ b/zerver/tests/test_audit_log.py @@ -81,7 +81,6 @@ from zerver.models import ( RealmPlayground, Recipient, Subscription, - SystemGroups, UserGroup, UserProfile, get_all_custom_emoji_for_realm, @@ -91,6 +90,7 @@ from zerver.models import ( get_stream, linkifiers_for_realm, ) +from zerver.models.groups import SystemGroups class TestRealmAuditLog(ZulipTestCase): diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py index 6c02651b29..237652f259 100644 --- a/zerver/tests/test_events.py +++ b/zerver/tests/test_events.py @@ -234,7 +234,6 @@ from zerver.models import ( RealmUserDefault, Service, Stream, - SystemGroups, UserGroup, UserMessage, UserPresence, @@ -244,6 +243,7 @@ from zerver.models import ( get_client, get_stream, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_user_by_delivery_email from zerver.openapi.openapi import validate_against_openapi_schema from zerver.tornado.django_api import send_event diff --git a/zerver/tests/test_import_export.py b/zerver/tests/test_import_export.py index a701a7b63d..4d32e1b911 100644 --- a/zerver/tests/test_import_export.py +++ b/zerver/tests/test_import_export.py @@ -76,7 +76,6 @@ from zerver.models import ( ScheduledMessage, Stream, Subscription, - SystemGroups, UserGroup, UserGroupMembership, UserMessage, @@ -90,6 +89,7 @@ from zerver.models import ( get_realm, get_stream, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_system_bot, get_user_by_delivery_email diff --git a/zerver/tests/test_invite.py b/zerver/tests/test_invite.py index c13f925c58..77595c1b80 100644 --- a/zerver/tests/test_invite.py +++ b/zerver/tests/test_invite.py @@ -60,13 +60,13 @@ from zerver.models import ( Realm, ScheduledEmail, Stream, - SystemGroups, UserGroup, UserMessage, UserProfile, get_realm, get_stream, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_user_by_delivery_email from zerver.views.invite import INVITATION_LINK_VALIDITY_MINUTES, get_invitee_emails_set from zerver.views.registration import accounts_home diff --git a/zerver/tests/test_markdown.py b/zerver/tests/test_markdown.py index e1002ac7c7..514f328637 100644 --- a/zerver/tests/test_markdown.py +++ b/zerver/tests/test_markdown.py @@ -64,7 +64,6 @@ from zerver.models import ( Message, RealmEmoji, RealmFilter, - SystemGroups, UserGroup, UserMessage, UserProfile, @@ -73,6 +72,7 @@ from zerver.models import ( get_stream, linkifiers_for_realm, ) +from zerver.models.groups import SystemGroups class SimulatedFencedBlockPreprocessor(FencedBlockPreprocessor): diff --git a/zerver/tests/test_message_edit.py b/zerver/tests/test_message_edit.py index 22d7622206..a23f6d7a2e 100644 --- a/zerver/tests/test_message_edit.py +++ b/zerver/tests/test_message_edit.py @@ -34,7 +34,6 @@ from zerver.models import ( Message, Realm, Stream, - SystemGroups, UserGroup, UserMessage, UserProfile, @@ -43,6 +42,7 @@ from zerver.models import ( get_stream, ) from zerver.models.constants import MAX_TOPIC_NAME_LENGTH +from zerver.models.groups import SystemGroups if TYPE_CHECKING: from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse diff --git a/zerver/tests/test_message_send.py b/zerver/tests/test_message_send.py index 525692fd4e..a523922005 100644 --- a/zerver/tests/test_message_send.py +++ b/zerver/tests/test_message_send.py @@ -53,7 +53,6 @@ from zerver.models import ( Recipient, Stream, Subscription, - SystemGroups, UserGroup, UserMessage, UserProfile, @@ -62,6 +61,7 @@ from zerver.models import ( get_stream, ) from zerver.models.constants import MAX_TOPIC_NAME_LENGTH +from zerver.models.groups import SystemGroups from zerver.models.users import get_system_bot, get_user from zerver.views.message_send import InvalidMirrorInputError diff --git a/zerver/tests/test_realm.py b/zerver/tests/test_realm.py index e989f4d0bf..837f5d4fa0 100644 --- a/zerver/tests/test_realm.py +++ b/zerver/tests/test_realm.py @@ -49,7 +49,6 @@ from zerver.models import ( RealmUserDefault, ScheduledEmail, Stream, - SystemGroups, UserGroup, UserGroupMembership, UserMessage, @@ -57,6 +56,7 @@ from zerver.models import ( get_realm, get_stream, ) +from zerver.models.groups import SystemGroups from zerver.models.users import get_system_bot, get_user_profile_by_id diff --git a/zerver/tests/test_user_groups.py b/zerver/tests/test_user_groups.py index a1126b53f1..dde4dced70 100644 --- a/zerver/tests/test_user_groups.py +++ b/zerver/tests/test_user_groups.py @@ -35,12 +35,12 @@ from zerver.lib.user_groups import ( from zerver.models import ( GroupGroupMembership, Realm, - SystemGroups, UserGroup, UserGroupMembership, UserProfile, get_realm, ) +from zerver.models.groups import SystemGroups class UserGroupTestCase(ZulipTestCase): diff --git a/zerver/tests/test_users.py b/zerver/tests/test_users.py index d5e7272f96..91ee3a0701 100644 --- a/zerver/tests/test_users.py +++ b/zerver/tests/test_users.py @@ -70,7 +70,6 @@ from zerver.models import ( ScheduledEmail, Stream, Subscription, - SystemGroups, UserGroupMembership, UserProfile, UserTopic, @@ -81,6 +80,7 @@ from zerver.models import ( get_realm, get_stream, ) +from zerver.models.groups import SystemGroups from zerver.models.users import ( get_source_profile, get_system_bot,