mirror of
https://github.com/zulip/zulip.git
synced 2025-11-16 20:02:15 +00:00
realm: Create role-based system user groups on creating realm.
We create system user groups for following roles - owners, admins, moderators, members and guests. Full members user group will be handled separately.
This commit is contained in:
@@ -193,7 +193,11 @@ from zerver.lib.upload import (
|
||||
delete_message_image,
|
||||
upload_emoji_image,
|
||||
)
|
||||
from zerver.lib.user_groups import access_user_group_by_id, create_user_group
|
||||
from zerver.lib.user_groups import (
|
||||
access_user_group_by_id,
|
||||
create_system_user_groups_for_realm,
|
||||
create_user_group,
|
||||
)
|
||||
from zerver.lib.user_mutes import add_user_mute, get_muting_users, get_user_mutes
|
||||
from zerver.lib.user_status import update_user_status
|
||||
from zerver.lib.user_topics import add_topic_mute, get_topic_mutes, remove_topic_mute
|
||||
@@ -5534,6 +5538,8 @@ def do_create_realm(
|
||||
|
||||
RealmUserDefault.objects.create(realm=realm)
|
||||
|
||||
create_system_user_groups_for_realm(realm)
|
||||
|
||||
# Create stream once Realm object has been saved
|
||||
notifications_stream = ensure_stream(
|
||||
realm,
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.utils.translation import gettext as _
|
||||
from django_cte import With
|
||||
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.models import Realm, UserGroup, UserGroupMembership, UserProfile
|
||||
from zerver.models import GroupGroupMembership, Realm, UserGroup, UserGroupMembership, UserProfile
|
||||
|
||||
|
||||
def access_user_group_by_id(user_group_id: int, user_profile: UserProfile) -> UserGroup:
|
||||
@@ -126,3 +126,51 @@ def get_recursive_membership_groups(user_profile: UserProfile) -> "QuerySet[User
|
||||
)
|
||||
)
|
||||
return cte.join(UserGroup, id=cte.col.id).with_cte(cte)
|
||||
|
||||
|
||||
def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
|
||||
role_system_groups_dict: Dict[int, UserGroup] = {}
|
||||
for role in UserGroup.SYSTEM_USER_GROUP_ROLE_MAP.keys():
|
||||
user_group_params = UserGroup.SYSTEM_USER_GROUP_ROLE_MAP[role]
|
||||
user_group = UserGroup(
|
||||
name=user_group_params["name"],
|
||||
description=user_group_params["description"],
|
||||
realm=realm,
|
||||
is_system_group=True,
|
||||
)
|
||||
role_system_groups_dict[role] = user_group
|
||||
|
||||
full_members_system_group = UserGroup(
|
||||
name="@role:fullmembers",
|
||||
description="Members of this organization, not including new accounts and guests",
|
||||
realm=realm,
|
||||
is_system_group=True,
|
||||
)
|
||||
everyone_on_internet_system_group = UserGroup(
|
||||
name="@role:internet",
|
||||
description="Everyone on the Internet",
|
||||
realm=realm,
|
||||
is_system_group=True,
|
||||
)
|
||||
# Order of this list here is important to create correct GroupGroupMembership objects
|
||||
system_user_groups_list = [
|
||||
role_system_groups_dict[UserProfile.ROLE_REALM_OWNER],
|
||||
role_system_groups_dict[UserProfile.ROLE_REALM_ADMINISTRATOR],
|
||||
role_system_groups_dict[UserProfile.ROLE_MODERATOR],
|
||||
full_members_system_group,
|
||||
role_system_groups_dict[UserProfile.ROLE_MEMBER],
|
||||
role_system_groups_dict[UserProfile.ROLE_GUEST],
|
||||
everyone_on_internet_system_group,
|
||||
]
|
||||
|
||||
UserGroup.objects.bulk_create(system_user_groups_list)
|
||||
|
||||
subgroup_objects = []
|
||||
subgroup, remaining_groups = system_user_groups_list[0], system_user_groups_list[1:]
|
||||
for supergroup in remaining_groups:
|
||||
subgroup_objects.append(GroupGroupMembership(subgroup=subgroup, supergroup=supergroup))
|
||||
subgroup = supergroup
|
||||
|
||||
GroupGroupMembership.objects.bulk_create(subgroup_objects)
|
||||
|
||||
return role_system_groups_dict
|
||||
|
||||
@@ -2108,6 +2108,31 @@ class UserGroup(models.Model):
|
||||
description: str = models.TextField(default="")
|
||||
is_system_group: bool = models.BooleanField(default=False)
|
||||
|
||||
# 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": "@role:owners",
|
||||
"description": "Owners of this organization",
|
||||
},
|
||||
UserProfile.ROLE_REALM_ADMINISTRATOR: {
|
||||
"name": "@role:administrators",
|
||||
"description": "Administrators of this organization, including owners",
|
||||
},
|
||||
UserProfile.ROLE_MODERATOR: {
|
||||
"name": "@role:moderators",
|
||||
"description": "Moderators of this organization, including administrators",
|
||||
},
|
||||
UserProfile.ROLE_MEMBER: {
|
||||
"name": "@role:members",
|
||||
"description": "Members of this organization, not including guests",
|
||||
},
|
||||
UserProfile.ROLE_GUEST: {
|
||||
"name": "@role:everyone",
|
||||
"description": "Everyone in this organization, including guests",
|
||||
},
|
||||
}
|
||||
|
||||
class Meta:
|
||||
unique_together = (("realm", "name"),)
|
||||
|
||||
|
||||
@@ -15658,7 +15658,7 @@ components:
|
||||
The ID of the target user group.
|
||||
schema:
|
||||
type: integer
|
||||
example: 1
|
||||
example: 22
|
||||
required: true
|
||||
QueueId:
|
||||
name: queue_id
|
||||
|
||||
@@ -2428,12 +2428,11 @@ class MarkdownTest(ZulipTestCase):
|
||||
def test_system_user_group_mention(self) -> None:
|
||||
desdemona = self.example_user("desdemona")
|
||||
iago = self.example_user("iago")
|
||||
shiva = self.example_user("shiva")
|
||||
hamlet = self.example_user("hamlet")
|
||||
moderators_group = create_user_group(
|
||||
"Moderators", [iago, shiva], get_realm("zulip"), is_system_group=True
|
||||
moderators_group = UserGroup.objects.get(
|
||||
realm=iago.realm, name="@role:moderators", is_system_group=True
|
||||
)
|
||||
content = "@*Moderators* @**King Hamlet** test message"
|
||||
content = "@*role:moderators* @**King Hamlet** test message"
|
||||
|
||||
# Owner cannot mention a system user group.
|
||||
msg = Message(sender=desdemona, sending_client=get_client("test"))
|
||||
|
||||
@@ -34,6 +34,7 @@ from zerver.models import (
|
||||
RealmUserDefault,
|
||||
ScheduledEmail,
|
||||
Stream,
|
||||
UserGroup,
|
||||
UserMessage,
|
||||
UserProfile,
|
||||
get_realm,
|
||||
@@ -888,6 +889,23 @@ class RealmTest(ZulipTestCase):
|
||||
self.assertEqual(realm.web_public_streams_enabled(), False)
|
||||
self.assertEqual(realm.has_web_public_streams(), False)
|
||||
|
||||
def test_creating_realm_creates_system_groups(self) -> None:
|
||||
realm = do_create_realm("realm_string_id", "realm name")
|
||||
system_user_groups = UserGroup.objects.filter(realm=realm, is_system_group=True)
|
||||
|
||||
self.assert_length(system_user_groups, 7)
|
||||
user_group_names = [group.name for group in system_user_groups]
|
||||
expected_system_group_names = [
|
||||
"@role:owners",
|
||||
"@role:administrators",
|
||||
"@role:moderators",
|
||||
"@role:fullmembers",
|
||||
"@role:members",
|
||||
"@role:everyone",
|
||||
"@role:internet",
|
||||
]
|
||||
self.assertEqual(user_group_names.sort(), expected_system_group_names.sort())
|
||||
|
||||
|
||||
class RealmAPITest(ZulipTestCase):
|
||||
def setUp(self) -> None:
|
||||
|
||||
@@ -42,16 +42,16 @@ class UserGroupTestCase(ZulipTestCase):
|
||||
empty_user_group = create_user_group("newgroup", [], realm)
|
||||
|
||||
user_groups = user_groups_in_realm_serialized(realm)
|
||||
self.assert_length(user_groups, 2)
|
||||
self.assert_length(user_groups, 9)
|
||||
self.assertEqual(user_groups[0]["id"], user_group.id)
|
||||
self.assertEqual(user_groups[0]["name"], "hamletcharacters")
|
||||
self.assertEqual(user_groups[0]["description"], "Characters of Hamlet")
|
||||
self.assertEqual(user_groups[0]["name"], "@role:owners")
|
||||
self.assertEqual(user_groups[0]["description"], "Owners of this organization")
|
||||
self.assertEqual(set(user_groups[0]["members"]), set(membership))
|
||||
|
||||
self.assertEqual(user_groups[1]["id"], empty_user_group.id)
|
||||
self.assertEqual(user_groups[1]["name"], "newgroup")
|
||||
self.assertEqual(user_groups[1]["description"], "")
|
||||
self.assertEqual(user_groups[1]["members"], [])
|
||||
self.assertEqual(user_groups[8]["id"], empty_user_group.id)
|
||||
self.assertEqual(user_groups[8]["name"], "newgroup")
|
||||
self.assertEqual(user_groups[8]["description"], "")
|
||||
self.assertEqual(user_groups[8]["members"], [])
|
||||
|
||||
def test_get_direct_user_groups(self) -> None:
|
||||
othello = self.example_user("othello")
|
||||
@@ -101,6 +101,68 @@ class UserGroupTestCase(ZulipTestCase):
|
||||
)
|
||||
self.assertCountEqual(list(get_recursive_membership_groups(shiva)), [everyone_group])
|
||||
|
||||
def test_subgroups_of_role_based_system_groups(self) -> None:
|
||||
realm = get_realm("zulip")
|
||||
owners_group = UserGroup.objects.get(realm=realm, name="@role:owners", is_system_group=True)
|
||||
admins_group = UserGroup.objects.get(
|
||||
realm=realm, name="@role:administrators", is_system_group=True
|
||||
)
|
||||
moderators_group = UserGroup.objects.get(
|
||||
realm=realm, name="@role:moderators", is_system_group=True
|
||||
)
|
||||
full_members_group = UserGroup.objects.get(
|
||||
realm=realm, name="@role:fullmembers", is_system_group=True
|
||||
)
|
||||
members_group = UserGroup.objects.get(
|
||||
realm=realm, name="@role:members", is_system_group=True
|
||||
)
|
||||
everyone_group = UserGroup.objects.get(
|
||||
realm=realm, name="@role:everyone", is_system_group=True
|
||||
)
|
||||
everyone_on_internet_group = UserGroup.objects.get(
|
||||
realm=realm, name="@role:internet", is_system_group=True
|
||||
)
|
||||
|
||||
self.assertCountEqual(list(get_recursive_subgroups(owners_group)), [owners_group])
|
||||
self.assertCountEqual(
|
||||
list(get_recursive_subgroups(admins_group)), [owners_group, admins_group]
|
||||
)
|
||||
self.assertCountEqual(
|
||||
list(get_recursive_subgroups(moderators_group)),
|
||||
[owners_group, admins_group, moderators_group],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
list(get_recursive_subgroups(full_members_group)),
|
||||
[owners_group, admins_group, moderators_group, full_members_group],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
list(get_recursive_subgroups(members_group)),
|
||||
[owners_group, admins_group, moderators_group, full_members_group, members_group],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
list(get_recursive_subgroups(everyone_group)),
|
||||
[
|
||||
owners_group,
|
||||
admins_group,
|
||||
moderators_group,
|
||||
full_members_group,
|
||||
members_group,
|
||||
everyone_group,
|
||||
],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
list(get_recursive_subgroups(everyone_on_internet_group)),
|
||||
[
|
||||
owners_group,
|
||||
admins_group,
|
||||
moderators_group,
|
||||
full_members_group,
|
||||
members_group,
|
||||
everyone_group,
|
||||
everyone_on_internet_group,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class UserGroupAPITestCase(UserGroupTestCase):
|
||||
def test_user_group_create(self) -> None:
|
||||
@@ -115,7 +177,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
}
|
||||
result = self.client_post("/json/user_groups/create", info=params)
|
||||
self.assert_json_success(result)
|
||||
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 2)
|
||||
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 9)
|
||||
|
||||
# Test invalid member error
|
||||
params = {
|
||||
@@ -125,7 +187,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
}
|
||||
result = self.client_post("/json/user_groups/create", info=params)
|
||||
self.assert_json_error(result, "Invalid user ID: 1111")
|
||||
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 2)
|
||||
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 9)
|
||||
|
||||
# Test we cannot create group with same name again
|
||||
params = {
|
||||
@@ -135,7 +197,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
}
|
||||
result = self.client_post("/json/user_groups/create", info=params)
|
||||
self.assert_json_error(result, "User group 'support' already exists.")
|
||||
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 2)
|
||||
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 9)
|
||||
|
||||
def test_user_group_get(self) -> None:
|
||||
# Test success
|
||||
@@ -205,11 +267,11 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
self.client_post("/json/user_groups/create", info=params)
|
||||
user_group = UserGroup.objects.get(name="support")
|
||||
# Test success
|
||||
self.assertEqual(UserGroup.objects.filter(realm=hamlet.realm).count(), 2)
|
||||
self.assertEqual(UserGroup.objects.filter(realm=hamlet.realm).count(), 9)
|
||||
self.assertEqual(UserGroupMembership.objects.count(), 3)
|
||||
result = self.client_delete(f"/json/user_groups/{user_group.id}")
|
||||
self.assert_json_success(result)
|
||||
self.assertEqual(UserGroup.objects.filter(realm=hamlet.realm).count(), 1)
|
||||
self.assertEqual(UserGroup.objects.filter(realm=hamlet.realm).count(), 8)
|
||||
self.assertEqual(UserGroupMembership.objects.count(), 2)
|
||||
# Test when invalid user group is supplied
|
||||
result = self.client_delete("/json/user_groups/1111")
|
||||
@@ -333,7 +395,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
if error_msg is None:
|
||||
self.assert_json_success(result)
|
||||
# One group already exists in the test database.
|
||||
self.assert_length(UserGroup.objects.filter(realm=realm), 2)
|
||||
self.assert_length(UserGroup.objects.filter(realm=realm), 9)
|
||||
else:
|
||||
self.assert_json_error(result, error_msg)
|
||||
|
||||
@@ -343,7 +405,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
result = self.client_delete(f"/json/user_groups/{user_group.id}")
|
||||
if error_msg is None:
|
||||
self.assert_json_success(result)
|
||||
self.assert_length(UserGroup.objects.filter(realm=realm), 1)
|
||||
self.assert_length(UserGroup.objects.filter(realm=realm), 8)
|
||||
else:
|
||||
self.assert_json_error(result, error_msg)
|
||||
|
||||
@@ -637,14 +699,9 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
||||
iago = self.example_user("iago")
|
||||
othello = self.example_user("othello")
|
||||
aaron = self.example_user("aaron")
|
||||
members = [iago, othello]
|
||||
|
||||
user_group = create_user_group(
|
||||
"Full members",
|
||||
members,
|
||||
iago.realm,
|
||||
description="Full members user group",
|
||||
is_system_group=True,
|
||||
user_group = UserGroup.objects.get(
|
||||
realm=iago.realm, name="@role:fullmembers", is_system_group=True
|
||||
)
|
||||
|
||||
def check_support_group_permission(acting_user: UserProfile) -> None:
|
||||
|
||||
Reference in New Issue
Block a user