mirror of https://github.com/zulip/zulip.git
streams: Allow changing can_remove_subscribers_group through API.
This commit adds API support to change can_remove_subscribers_group setting for a stream.
This commit is contained in:
parent
6fa0a83d6b
commit
c3759814be
|
@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
|
|||
|
||||
## Changes in Zulip 7.0
|
||||
|
||||
**Feature level 161**
|
||||
|
||||
* [`PATCH /streams/{stream_id}`](/api/update-stream): Added
|
||||
`can_remove_subscribers_group_id` parameter to support
|
||||
changing `can_remove_subscribers_group` setting.
|
||||
|
||||
**Feature level 160**
|
||||
|
||||
* [`POST /api/v1/jwt/fetch_api_key`]: New API endpoint to fetch API
|
||||
|
|
|
@ -58,6 +58,33 @@ def access_user_groups_as_potential_subgroups(
|
|||
return list(user_groups)
|
||||
|
||||
|
||||
def access_user_group_for_setting(
|
||||
user_group_id: int,
|
||||
user_profile: UserProfile,
|
||||
*,
|
||||
setting_name: str,
|
||||
require_system_group: bool = False,
|
||||
allow_internet_group: bool = False,
|
||||
allow_owners_group: bool = False,
|
||||
) -> UserGroup:
|
||||
user_group = access_user_group_by_id(user_group_id, user_profile, for_read=True)
|
||||
|
||||
if require_system_group and not user_group.is_system_group:
|
||||
raise JsonableError(_("'{}' must be a system user group.").format(setting_name))
|
||||
|
||||
if not allow_internet_group and user_group.name == UserGroup.EVERYONE_ON_INTERNET_GROUP_NAME:
|
||||
raise JsonableError(
|
||||
_("'{}' setting cannot be set to '@role:internet' group.").format(setting_name)
|
||||
)
|
||||
|
||||
if not allow_owners_group and user_group.name == UserGroup.OWNERS_GROUP_NAME:
|
||||
raise JsonableError(
|
||||
_("'{}' setting cannot be set to '@role:owners' group.").format(setting_name)
|
||||
)
|
||||
|
||||
return user_group
|
||||
|
||||
|
||||
def user_groups_in_realm_serialized(realm: Realm) -> List[UserGroupDict]:
|
||||
"""This function is used in do_events_register code path so this code
|
||||
should be performant. We need to do 2 database queries because
|
||||
|
|
|
@ -8027,14 +8027,23 @@ paths:
|
|||
Unsubscribe yourself or other users from one or more streams.
|
||||
|
||||
In addition to managing the current user's subscriptions, this
|
||||
endpoint can be used by organization administrators to remove
|
||||
other users from streams, or to remove a bot that the current
|
||||
user is the `bot_owner` for from any stream that the current
|
||||
user can access.
|
||||
endpoint can be used to remove other users from streams. This
|
||||
is possible in 3 situations:
|
||||
|
||||
**Changes**: Before Zulip 6.0 (feature level 145), only
|
||||
organization administrators could remove other users from
|
||||
streams.
|
||||
- Organization administrators can remove any user from any
|
||||
stream.
|
||||
- Users can remove a bot that they own from any stream that
|
||||
the user can access.
|
||||
- Users who can access a stream and are in the group with ID
|
||||
`can_remove_subscribers_group_id` for that stream can
|
||||
unsubscribe any user from that stream.
|
||||
|
||||
**Changes**: Before Zulip 7.0 (feature level 161),
|
||||
`can_remove_subscribers_group_id` was always the system group
|
||||
for organization administrators.
|
||||
|
||||
Before Zulip 6.0 (feature level 145), users had no special
|
||||
privileges for managing bots that they own.
|
||||
x-curl-examples-parameters:
|
||||
oneOf:
|
||||
- type: include
|
||||
|
@ -14536,6 +14545,7 @@ paths:
|
|||
required: false
|
||||
- $ref: "#/components/parameters/StreamPostPolicy"
|
||||
- $ref: "#/components/parameters/MessageRetentionDays"
|
||||
- $ref: "#/components/parameters/CanRemoveSubscribersGroupId"
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/SimpleSuccess"
|
||||
|
@ -17562,6 +17572,22 @@ components:
|
|||
- type: integer
|
||||
example: "20"
|
||||
required: false
|
||||
CanRemoveSubscribersGroupId:
|
||||
name: can_remove_subscribers_group_id
|
||||
in: query
|
||||
description: |
|
||||
ID of the user group whose members are allowed to unsubscribe others
|
||||
from the stream, if they have access to this stream, even if
|
||||
they are not an organization administrator.
|
||||
|
||||
This setting can currently only be set to system user groups
|
||||
except `@role:internet` and `@role:owners` group.
|
||||
|
||||
**Changes**: New in Zulip 7.0 (feature level 161).
|
||||
schema:
|
||||
type: integer
|
||||
example: 20
|
||||
required: false
|
||||
LinkifierPattern:
|
||||
name: pattern
|
||||
in: query
|
||||
|
|
|
@ -1977,6 +1977,80 @@ class StreamAdminTest(ZulipTestCase):
|
|||
stream = get_stream("stream_name1", realm)
|
||||
self.assertEqual(stream.message_retention_days, 2)
|
||||
|
||||
def test_change_stream_can_remove_subscribers_group(self) -> None:
|
||||
user_profile = self.example_user("iago")
|
||||
realm = user_profile.realm
|
||||
stream = self.subscribe(user_profile, "stream_name1")
|
||||
|
||||
moderators_system_group = UserGroup.objects.get(
|
||||
name="@role:moderators", realm=realm, is_system_group=True
|
||||
)
|
||||
self.login("shiva")
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(moderators_system_group.id).decode()},
|
||||
)
|
||||
self.assert_json_error(result, "Must be an organization administrator")
|
||||
|
||||
self.login("iago")
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(moderators_system_group.id).decode()},
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
stream = get_stream("stream_name1", realm)
|
||||
self.assertEqual(stream.can_remove_subscribers_group.id, moderators_system_group.id)
|
||||
|
||||
# This setting can only be set to system groups.
|
||||
hamletcharacters_group = UserGroup.objects.get(name="hamletcharacters", realm=realm)
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(hamletcharacters_group.id).decode()},
|
||||
)
|
||||
self.assert_json_error(
|
||||
result, "'can_remove_subscribers_group' must be a system user group."
|
||||
)
|
||||
|
||||
internet_group = UserGroup.objects.get(
|
||||
name="@role:internet", is_system_group=True, realm=realm
|
||||
)
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(internet_group.id).decode()},
|
||||
)
|
||||
self.assert_json_error(
|
||||
result,
|
||||
"'can_remove_subscribers_group' setting cannot be set to '@role:internet' group.",
|
||||
)
|
||||
|
||||
owners_group = UserGroup.objects.get(name="@role:owners", is_system_group=True, realm=realm)
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(owners_group.id).decode()},
|
||||
)
|
||||
self.assert_json_error(
|
||||
result,
|
||||
"'can_remove_subscribers_group' setting cannot be set to '@role:owners' group.",
|
||||
)
|
||||
|
||||
# For private streams, even admins must be subscribed to the stream to change
|
||||
# can_remove_subscribers_group setting.
|
||||
stream = self.make_stream("stream_name2", invite_only=True)
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(moderators_system_group.id).decode()},
|
||||
)
|
||||
self.assert_json_error(result, "Invalid stream ID")
|
||||
|
||||
self.subscribe(user_profile, "stream_name2")
|
||||
result = self.client_patch(
|
||||
f"/json/streams/{stream.id}",
|
||||
{"can_remove_subscribers_group_id": orjson.dumps(moderators_system_group.id).decode()},
|
||||
)
|
||||
self.assert_json_success(result)
|
||||
stream = get_stream("stream_name2", realm)
|
||||
self.assertEqual(stream.can_remove_subscribers_group.id, moderators_system_group.id)
|
||||
|
||||
def test_stream_message_retention_days_on_stream_creation(self) -> None:
|
||||
"""
|
||||
Only admins can create streams with message_retention_days
|
||||
|
|
|
@ -30,6 +30,7 @@ from zerver.actions.message_send import (
|
|||
from zerver.actions.streams import (
|
||||
bulk_add_subscriptions,
|
||||
bulk_remove_subscriptions,
|
||||
do_change_can_remove_subscribers_group,
|
||||
do_change_stream_description,
|
||||
do_change_stream_message_retention_days,
|
||||
do_change_stream_permission,
|
||||
|
@ -79,6 +80,7 @@ from zerver.lib.topic import (
|
|||
messages_for_topic,
|
||||
)
|
||||
from zerver.lib.types import Validator
|
||||
from zerver.lib.user_groups import access_user_group_for_setting
|
||||
from zerver.lib.utils import assert_is_not_none
|
||||
from zerver.lib.validator import (
|
||||
check_bool,
|
||||
|
@ -266,6 +268,7 @@ def update_stream_backend(
|
|||
message_retention_days: Optional[Union[int, str]] = REQ(
|
||||
json_validator=check_string_or_int, default=None
|
||||
),
|
||||
can_remove_subscribers_group_id: Optional[int] = REQ(json_validator=check_int, default=None),
|
||||
) -> HttpResponse:
|
||||
# We allow realm administrators to to update the stream name and
|
||||
# description even for private streams.
|
||||
|
@ -380,6 +383,20 @@ def update_stream_backend(
|
|||
if stream_post_policy is not None:
|
||||
do_change_stream_post_policy(stream, stream_post_policy, acting_user=user_profile)
|
||||
|
||||
if can_remove_subscribers_group_id is not None:
|
||||
if sub is None and stream.invite_only:
|
||||
# Admins cannot change this setting for unsubscribed private streams.
|
||||
raise JsonableError(_("Invalid stream ID"))
|
||||
|
||||
user_group = access_user_group_for_setting(
|
||||
can_remove_subscribers_group_id,
|
||||
user_profile,
|
||||
setting_name="can_remove_subscribers_group",
|
||||
require_system_group=True,
|
||||
)
|
||||
|
||||
do_change_can_remove_subscribers_group(stream, user_group, acting_user=user_profile)
|
||||
|
||||
return json_success(request)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue