user_groups: Add "Nobody" system user group.

This commit adds code to create a "Nobody" system user group
to realms which will be used in settings to represent "Nobody"
option.

We also add a migration to add this group to existing realms.
This commit is contained in:
Sahil Batra
2023-03-27 08:58:12 +05:30
committed by Tim Abbott
parent ecead64718
commit bed2bf64c4
7 changed files with 95 additions and 28 deletions

View File

@@ -237,8 +237,15 @@ def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
realm=realm,
is_system_group=True,
)
nobody_system_group = UserGroup(
name=UserGroup.NOBODY_GROUP_NAME,
description="Nobody",
realm=realm,
is_system_group=True,
)
# Order of this list here is important to create correct GroupGroupMembership objects
system_user_groups_list = [
nobody_system_group,
role_system_groups_dict[UserProfile.ROLE_REALM_OWNER],
role_system_groups_dict[UserProfile.ROLE_REALM_ADMINISTRATOR],
role_system_groups_dict[UserProfile.ROLE_MODERATOR],
@@ -251,7 +258,8 @@ def create_system_user_groups_for_realm(realm: Realm) -> Dict[int, UserGroup]:
UserGroup.objects.bulk_create(system_user_groups_list)
subgroup_objects = []
subgroup, remaining_groups = system_user_groups_list[0], system_user_groups_list[1:]
# "Nobody" system group is not a subgroup of any user group, since it is already empty.
subgroup, remaining_groups = system_user_groups_list[1], system_user_groups_list[2:]
for supergroup in remaining_groups:
subgroup_objects.append(GroupGroupMembership(subgroup=subgroup, supergroup=supergroup))
subgroup = supergroup

View File

@@ -0,0 +1,50 @@
# Generated by Django 4.1.7 on 2023-03-27 03:00
from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps
def create_nobody_system_user_group_for_existing_realms(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
Realm = apps.get_model("zerver", "Realm")
UserGroup = apps.get_model("zerver", "UserGroup")
NOBODY_GROUP_NAME = "@role:nobody"
NOBODY_GROUP_DESCRIPTION = "Nobody"
groups_to_create = []
for realm in Realm.objects.all():
groups_to_create.append(
UserGroup(
name=NOBODY_GROUP_NAME,
description=NOBODY_GROUP_DESCRIPTION,
realm=realm,
is_system_group=True,
)
)
UserGroup.objects.bulk_create(groups_to_create)
def delete_nobody_system_user_groups(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
UserGroup = apps.get_model("zerver", "UserGroup")
NOBODY_GROUP_NAME = "@role:nobody"
UserGroup.objects.filter(name=NOBODY_GROUP_NAME, is_system_group=True).delete()
class Migration(migrations.Migration):
dependencies = [
("zerver", "0433_preregistrationrealm"),
]
operations = [
migrations.RunPython(
create_nobody_system_user_group_for_existing_realms,
reverse_code=delete_nobody_system_user_groups,
elidable=True,
),
]

View File

@@ -2224,6 +2224,7 @@ class UserGroup(models.Model): # type: ignore[django-manager-missing] # django-
MODERATORS_GROUP_NAME = "@role:moderators"
MEMBERS_GROUP_NAME = "@role:members"
EVERYONE_GROUP_NAME = "@role:everyone"
NOBODY_GROUP_NAME = "@role:nobody"
# We do not have "Full members" and "Everyone on the internet"
# group here since there isn't a separate role value for full

View File

@@ -15293,7 +15293,7 @@ paths:
type: array
items:
type: integer
example: [8, 9]
example: [9, 10]
required: false
responses:
"200":
@@ -17605,7 +17605,7 @@ components:
The ID of the target user group.
schema:
type: integer
example: 29
example: 33
required: true
QueueId:
name: queue_id

View File

@@ -435,10 +435,10 @@ class RealmImportExportTest(ExportFile):
self.assertEqual(exported_realm_user_default[0]["default_language"], "de")
exported_usergroups = data["zerver_usergroup"]
self.assert_length(exported_usergroups, 8)
self.assertEqual(exported_usergroups[1]["name"], "@role:administrators")
self.assertFalse("direct_members" in exported_usergroups[1])
self.assertFalse("direct_subgroups" in exported_usergroups[1])
self.assert_length(exported_usergroups, 9)
self.assertEqual(exported_usergroups[2]["name"], "@role:administrators")
self.assertFalse("direct_members" in exported_usergroups[2])
self.assertFalse("direct_subgroups" in exported_usergroups[2])
data = read_json("messages-000001.json")
um = UserMessage.objects.all()[0]

View File

@@ -977,7 +977,7 @@ class RealmTest(ZulipTestCase):
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)
self.assert_length(system_user_groups, 8)
user_group_names = [group.name for group in system_user_groups]
expected_system_group_names = [
UserGroup.OWNERS_GROUP_NAME,
@@ -987,6 +987,7 @@ class RealmTest(ZulipTestCase):
UserGroup.MEMBERS_GROUP_NAME,
UserGroup.EVERYONE_GROUP_NAME,
UserGroup.EVERYONE_ON_INTERNET_GROUP_NAME,
UserGroup.NOBODY_GROUP_NAME,
]
self.assertEqual(user_group_names.sort(), expected_system_group_names.sort())

View File

@@ -40,30 +40,37 @@ class UserGroupTestCase(ZulipTestCase):
realm = get_realm("zulip")
user_group = UserGroup.objects.filter(realm=realm).first()
assert user_group is not None
membership = UserGroupMembership.objects.filter(user_group=user_group).values_list(
"user_profile_id", flat=True
)
empty_user_group = check_add_user_group(realm, "newgroup", [], acting_user=None)
user_groups = user_groups_in_realm_serialized(realm)
self.assert_length(user_groups, 9)
self.assert_length(user_groups, 10)
self.assertEqual(user_groups[0]["id"], user_group.id)
self.assertEqual(user_groups[0]["name"], UserGroup.OWNERS_GROUP_NAME)
self.assertEqual(user_groups[0]["description"], "Owners of this organization")
self.assertEqual(set(user_groups[0]["members"]), set(membership))
self.assertEqual(user_groups[0]["name"], UserGroup.NOBODY_GROUP_NAME)
self.assertEqual(user_groups[0]["description"], "Nobody")
self.assertEqual(user_groups[0]["members"], [])
self.assertEqual(user_groups[0]["direct_subgroup_ids"], [])
owners_system_group = UserGroup.objects.get(name=UserGroup.OWNERS_GROUP_NAME, realm=realm)
membership = UserGroupMembership.objects.filter(user_group=owners_system_group).values_list(
"user_profile_id", flat=True
)
self.assertEqual(user_groups[1]["id"], owners_system_group.id)
self.assertEqual(user_groups[1]["name"], UserGroup.OWNERS_GROUP_NAME)
self.assertEqual(user_groups[1]["description"], "Owners of this organization")
self.assertEqual(set(user_groups[1]["members"]), set(membership))
self.assertEqual(user_groups[1]["direct_subgroup_ids"], [])
admins_system_group = UserGroup.objects.get(
name=UserGroup.ADMINISTRATORS_GROUP_NAME, realm=realm
)
self.assertEqual(user_groups[1]["id"], admins_system_group.id)
self.assertEqual(user_groups[2]["id"], admins_system_group.id)
# Check that owners system group is present in "direct_subgroup_ids"
self.assertEqual(user_groups[1]["direct_subgroup_ids"], [user_group.id])
self.assertEqual(user_groups[2]["direct_subgroup_ids"], [owners_system_group.id])
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"], [])
self.assertEqual(user_groups[9]["id"], empty_user_group.id)
self.assertEqual(user_groups[9]["name"], "newgroup")
self.assertEqual(user_groups[9]["description"], "")
self.assertEqual(user_groups[9]["members"], [])
def test_get_direct_user_groups(self) -> None:
othello = self.example_user("othello")
@@ -221,7 +228,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), 9)
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 10)
# Test invalid member error
params = {
@@ -231,7 +238,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), 9)
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 10)
# Test we cannot create group with same name again
params = {
@@ -241,7 +248,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), 9)
self.assert_length(UserGroup.objects.filter(realm=hamlet.realm), 10)
def test_user_group_get(self) -> None:
# Test success
@@ -323,11 +330,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(), 9)
self.assertEqual(UserGroup.objects.filter(realm=hamlet.realm).count(), 10)
self.assertEqual(UserGroupMembership.objects.count(), 47)
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(), 8)
self.assertEqual(UserGroup.objects.filter(realm=hamlet.realm).count(), 9)
self.assertEqual(UserGroupMembership.objects.count(), 46)
# Test when invalid user group is supplied
result = self.client_delete("/json/user_groups/1111")
@@ -456,7 +463,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), 9)
self.assert_length(UserGroup.objects.filter(realm=realm), 10)
else:
self.assert_json_error(result, error_msg)
@@ -466,7 +473,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), 8)
self.assert_length(UserGroup.objects.filter(realm=realm), 9)
else:
self.assert_json_error(result, error_msg)