mirror of
https://github.com/zulip/zulip.git
synced 2025-11-13 02:17:19 +00:00
user_groups: Set can_manage_all_groups to administrator group.
Earlier we use to restrict admins, moderators or members of a group to manage that group if they were part of the realm wide `can_manage_all_groups`. We will not do that anymore and even non-members of a group regardless of role can manage a group if they are part of `can_manage_all_groups`. See https://chat.zulip.org/#narrow/stream/101-design/topic/Group.20add.20members.20dropdown/near/1952902 to check more about the migration plan for which this is the last step.
This commit is contained in:
committed by
Tim Abbott
parent
9bbd6a7316
commit
c9d5276031
@@ -142,12 +142,7 @@ By default, [owners](/help/roles-and-permissions) in a Zulip
|
|||||||
organization can manage user groups. However, you can expand that
|
organization can manage user groups. However, you can expand that
|
||||||
ability to specific [roles](/help/roles-and-permissions).
|
ability to specific [roles](/help/roles-and-permissions).
|
||||||
|
|
||||||
<!-- TODO: Remove this after #25942 is resolved and we've removed
|
Guests cannot modify user groups.
|
||||||
the condition that only members can manage the group if they are
|
|
||||||
not admins or moderators. -->
|
|
||||||
Note that administrators and moderators can modify any user group,
|
|
||||||
while other organization members can only modify user groups to which
|
|
||||||
they belong. Guests cannot modify user groups.
|
|
||||||
|
|
||||||
{start_tabs}
|
{start_tabs}
|
||||||
|
|
||||||
|
|||||||
@@ -192,19 +192,9 @@ export function can_manage_user_group(group_id: number): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let can_manage_all_groups = user_can_manage_all_groups();
|
|
||||||
|
|
||||||
const group = user_groups.get_user_group_from_id(group_id);
|
const group = user_groups.get_user_group_from_id(group_id);
|
||||||
|
|
||||||
if (
|
if (user_can_manage_all_groups()) {
|
||||||
!current_user.is_admin &&
|
|
||||||
!current_user.is_moderator &&
|
|
||||||
!user_groups.is_direct_member_of(current_user.user_id, group_id)
|
|
||||||
) {
|
|
||||||
can_manage_all_groups = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (can_manage_all_groups) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -443,10 +443,12 @@ run_test("can_manage_user_group", ({override}) => {
|
|||||||
override(current_user, "user_id", 2);
|
override(current_user, "user_id", 2);
|
||||||
assert.ok(!settings_data.can_manage_user_group(students.id));
|
assert.ok(!settings_data.can_manage_user_group(students.id));
|
||||||
|
|
||||||
|
// User with role member and not part of the group.
|
||||||
override(realm, "realm_can_manage_all_groups", members.id);
|
override(realm, "realm_can_manage_all_groups", members.id);
|
||||||
override(current_user, "user_id", 3);
|
override(current_user, "user_id", 3);
|
||||||
assert.ok(!settings_data.can_manage_user_group(students.id));
|
assert.ok(settings_data.can_manage_user_group(students.id));
|
||||||
|
|
||||||
|
// User with role member and part of the group.
|
||||||
override(current_user, "user_id", 2);
|
override(current_user, "user_id", 2);
|
||||||
assert.ok(settings_data.can_manage_user_group(students.id));
|
assert.ok(settings_data.can_manage_user_group(students.id));
|
||||||
|
|
||||||
|
|||||||
@@ -132,24 +132,6 @@ def access_user_group_to_read_membership(user_group_id: int, realm: Realm) -> Na
|
|||||||
return get_user_group_by_id_in_realm(user_group_id, realm, for_read=True)
|
return get_user_group_by_id_in_realm(user_group_id, realm, for_read=True)
|
||||||
|
|
||||||
|
|
||||||
def check_permission_for_managing_all_groups(
|
|
||||||
user_group: UserGroup, user_profile: UserProfile
|
|
||||||
) -> bool:
|
|
||||||
"""
|
|
||||||
Given a user and a group in the same realm, checks if the user
|
|
||||||
can manage the group through the legacy can_manage_all_groups
|
|
||||||
permission, which is a permission that requires either certain roles
|
|
||||||
or membership in the group itself to be used.
|
|
||||||
"""
|
|
||||||
can_manage_all_groups = user_profile.can_manage_all_groups()
|
|
||||||
if can_manage_all_groups:
|
|
||||||
if user_profile.is_realm_admin or user_profile.is_moderator:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return is_user_in_group(user_group, user_profile)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def access_user_group_for_update(
|
def access_user_group_for_update(
|
||||||
user_group_id: int,
|
user_group_id: int,
|
||||||
user_profile: UserProfile,
|
user_profile: UserProfile,
|
||||||
@@ -173,10 +155,14 @@ def access_user_group_for_update(
|
|||||||
raise JsonableError(_("Insufficient permission"))
|
raise JsonableError(_("Insufficient permission"))
|
||||||
|
|
||||||
assert permission_setting in NamedUserGroup.GROUP_PERMISSION_SETTINGS
|
assert permission_setting in NamedUserGroup.GROUP_PERMISSION_SETTINGS
|
||||||
if permission_setting in [
|
if (
|
||||||
|
permission_setting
|
||||||
|
in [
|
||||||
"can_manage_group",
|
"can_manage_group",
|
||||||
"can_add_members_group",
|
"can_add_members_group",
|
||||||
] and check_permission_for_managing_all_groups(user_group, user_profile):
|
]
|
||||||
|
and user_profile.can_manage_all_groups()
|
||||||
|
):
|
||||||
return user_group
|
return user_group
|
||||||
|
|
||||||
user_has_permission = user_has_permission_for_group_setting(
|
user_has_permission = user_has_permission_for_group_setting(
|
||||||
|
|||||||
56
zerver/migrations/0602_remap_can_manage_all_groups.py
Normal file
56
zerver/migrations/0602_remap_can_manage_all_groups.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Generated by Django 5.0.9 on 2024-10-10 10:41
|
||||||
|
|
||||||
|
from django.db import migrations, transaction
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
from django.db.migrations.state import StateApps
|
||||||
|
from django.db.models import Max, Min, OuterRef
|
||||||
|
|
||||||
|
|
||||||
|
def remap_can_manage_all_groups_for_existing_realms(
|
||||||
|
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
|
||||||
|
) -> None:
|
||||||
|
Realm = apps.get_model("zerver", "Realm")
|
||||||
|
NamedUserGroup = apps.get_model("zerver", "NamedUserGroup")
|
||||||
|
BATCH_SIZE = 1000
|
||||||
|
max_id = NamedUserGroup.objects.aggregate(Max("id"))["id__max"]
|
||||||
|
|
||||||
|
if max_id is None:
|
||||||
|
# Do nothing if there are no user groups on the server.
|
||||||
|
return
|
||||||
|
|
||||||
|
lower_bound = NamedUserGroup.objects.aggregate(Min("id"))["id__min"]
|
||||||
|
|
||||||
|
while lower_bound <= max_id:
|
||||||
|
upper_bound = lower_bound + BATCH_SIZE - 1
|
||||||
|
print(f"Processing batch {lower_bound} to {upper_bound} for NamedUserGroup")
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
# Since can_manage_group, can_add_members_group, etc. have
|
||||||
|
# migrated to the nearest possible value from
|
||||||
|
# user_group_edit_policy, we want to set
|
||||||
|
# can_manage_all_groups to the most restrictive setting
|
||||||
|
# previously possible. We've chosen administrators as the
|
||||||
|
# value here since the highest possible
|
||||||
|
# user_group_edit_policy was with role administrators.
|
||||||
|
Realm.objects.update(
|
||||||
|
can_manage_all_groups=NamedUserGroup.objects.filter(
|
||||||
|
name="role:administrators", realm=OuterRef("id"), is_system_group=True
|
||||||
|
).values("pk")
|
||||||
|
)
|
||||||
|
|
||||||
|
lower_bound += BATCH_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
atomic = False
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0601_alter_namedusergroup_can_add_members_group"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
remap_can_manage_all_groups_for_existing_realms,
|
||||||
|
elidable=True,
|
||||||
|
reverse_code=migrations.RunPython.noop,
|
||||||
|
)
|
||||||
|
]
|
||||||
@@ -1291,10 +1291,6 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
acting_user=None,
|
acting_user=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.login("hamlet")
|
|
||||||
result = self.client_post(f"/json/user_groups/{support_group.id}/deactivate")
|
|
||||||
self.assert_json_error(result, "Insufficient permission")
|
|
||||||
|
|
||||||
self.login("othello")
|
self.login("othello")
|
||||||
result = self.client_post(f"/json/user_groups/{support_group.id}/deactivate")
|
result = self.client_post(f"/json/user_groups/{support_group.id}/deactivate")
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
@@ -1997,8 +1993,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
check_update_user_group("support1", "Support team - test", "iago")
|
check_update_user_group("support1", "Support team - test", "iago")
|
||||||
check_update_user_group("support", "Support team", "othello")
|
check_update_user_group("support", "Support team", "othello")
|
||||||
|
|
||||||
# Check only members are allowed to update the user group and only if belong to the
|
# Check only members are allowed to update the user group.
|
||||||
# user group.
|
|
||||||
members_group = NamedUserGroup.objects.get(name=SystemGroups.MEMBERS, realm=realm)
|
members_group = NamedUserGroup.objects.get(name=SystemGroups.MEMBERS, realm=realm)
|
||||||
do_change_realm_permission_group_setting(
|
do_change_realm_permission_group_setting(
|
||||||
realm,
|
realm,
|
||||||
@@ -2009,13 +2004,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
check_update_user_group(
|
check_update_user_group(
|
||||||
"help", "Troubleshooting team", "polonius", "Not allowed for guest users"
|
"help", "Troubleshooting team", "polonius", "Not allowed for guest users"
|
||||||
)
|
)
|
||||||
check_update_user_group(
|
check_update_user_group("help", "Troubleshooting team", "cordelia")
|
||||||
"help",
|
|
||||||
"Troubleshooting team",
|
|
||||||
"cordelia",
|
|
||||||
"Insufficient permission",
|
|
||||||
)
|
|
||||||
check_update_user_group("help", "Troubleshooting team", "othello")
|
|
||||||
|
|
||||||
# Check user who is member of a subgroup of the group being updated
|
# Check user who is member of a subgroup of the group being updated
|
||||||
# can also update the group.
|
# can also update the group.
|
||||||
@@ -2181,8 +2170,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
self.assert_json_error(result, error_msg)
|
self.assert_json_error(result, error_msg)
|
||||||
|
|
||||||
realm = get_realm("zulip")
|
realm = get_realm("zulip")
|
||||||
# Check only admins are allowed to add/remove users from the group. Admins are allowed even if
|
# Check only admins are allowed to add/remove users from the group.
|
||||||
# they are not a member of the group.
|
|
||||||
admins_group = NamedUserGroup.objects.get(name=SystemGroups.ADMINISTRATORS, realm=realm)
|
admins_group = NamedUserGroup.objects.get(name=SystemGroups.ADMINISTRATORS, realm=realm)
|
||||||
do_change_realm_permission_group_setting(
|
do_change_realm_permission_group_setting(
|
||||||
realm,
|
realm,
|
||||||
@@ -2196,8 +2184,7 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
check_removing_members_from_group("shiva", "Insufficient permission")
|
check_removing_members_from_group("shiva", "Insufficient permission")
|
||||||
check_removing_members_from_group("iago")
|
check_removing_members_from_group("iago")
|
||||||
|
|
||||||
# Check moderators are allowed to add/remove users from the group but not members. Moderators are
|
# Check moderators are allowed to add/remove users from the group but not members.
|
||||||
# allowed even if they are not a member of the group.
|
|
||||||
moderators_group = NamedUserGroup.objects.get(name=SystemGroups.MODERATORS, realm=realm)
|
moderators_group = NamedUserGroup.objects.get(name=SystemGroups.MODERATORS, realm=realm)
|
||||||
do_change_realm_permission_group_setting(
|
do_change_realm_permission_group_setting(
|
||||||
realm,
|
realm,
|
||||||
@@ -2258,15 +2245,18 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
acting_user=None,
|
acting_user=None,
|
||||||
)
|
)
|
||||||
check_adding_members_to_group("polonius", "Not allowed for guest users")
|
check_adding_members_to_group("polonius", "Not allowed for guest users")
|
||||||
check_adding_members_to_group("cordelia", "Insufficient permission")
|
|
||||||
check_adding_members_to_group("othello")
|
|
||||||
|
|
||||||
|
# User with role member but not part of the target group should
|
||||||
|
# be allowed to add members to the group if they are part of
|
||||||
|
# `can_manage_all_groups`.
|
||||||
|
check_adding_members_to_group("cordelia")
|
||||||
|
check_removing_members_from_group("cordelia")
|
||||||
|
|
||||||
|
check_adding_members_to_group("othello")
|
||||||
check_removing_members_from_group("polonius", "Not allowed for guest users")
|
check_removing_members_from_group("polonius", "Not allowed for guest users")
|
||||||
check_removing_members_from_group("cordelia", "Insufficient permission")
|
|
||||||
check_removing_members_from_group("othello")
|
check_removing_members_from_group("othello")
|
||||||
|
|
||||||
# Check only full members are allowed to add/remove users in the group and only if belong to the
|
# Check only full members are allowed to add/remove users in the group.
|
||||||
# user group.
|
|
||||||
full_members_group = NamedUserGroup.objects.get(name=SystemGroups.FULL_MEMBERS, realm=realm)
|
full_members_group = NamedUserGroup.objects.get(name=SystemGroups.FULL_MEMBERS, realm=realm)
|
||||||
do_change_realm_permission_group_setting(
|
do_change_realm_permission_group_setting(
|
||||||
realm,
|
realm,
|
||||||
@@ -2284,7 +2274,12 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
cordelia.date_joined = timezone_now() - timedelta(days=11)
|
cordelia.date_joined = timezone_now() - timedelta(days=11)
|
||||||
cordelia.save()
|
cordelia.save()
|
||||||
promote_new_full_members()
|
promote_new_full_members()
|
||||||
check_adding_members_to_group("cordelia", "Insufficient permission")
|
|
||||||
|
# Full members who are not part of the target group should
|
||||||
|
# be allowed to add members to the group if they are part of
|
||||||
|
# `can_manage_all_groups`.
|
||||||
|
check_adding_members_to_group("cordelia")
|
||||||
|
check_removing_members_from_group("cordelia")
|
||||||
|
|
||||||
othello.date_joined = timezone_now() - timedelta(days=11)
|
othello.date_joined = timezone_now() - timedelta(days=11)
|
||||||
othello.save()
|
othello.save()
|
||||||
@@ -2295,7 +2290,6 @@ class UserGroupAPITestCase(UserGroupTestCase):
|
|||||||
othello.save()
|
othello.save()
|
||||||
promote_new_full_members()
|
promote_new_full_members()
|
||||||
|
|
||||||
check_removing_members_from_group("cordelia", "Insufficient permission")
|
|
||||||
check_removing_members_from_group("othello", "Insufficient permission")
|
check_removing_members_from_group("othello", "Insufficient permission")
|
||||||
|
|
||||||
othello.date_joined = timezone_now() - timedelta(days=11)
|
othello.date_joined = timezone_now() - timedelta(days=11)
|
||||||
|
|||||||
Reference in New Issue
Block a user