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:
Sahil Batra
2021-08-11 18:40:17 +05:30
committed by Tim Abbott
parent f31bb6754b
commit c33ba4ed6e
7 changed files with 181 additions and 28 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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"),)

View File

@@ -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

View File

@@ -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"))

View File

@@ -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:

View File

@@ -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: