mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 16:43:57 +00:00
Adds new organization setting `can_set_delete_message_policy_group` for defining who can set per-channel message delete permissions. Fixes #34214.
1476 lines
62 KiB
Python
1476 lines
62 KiB
Python
from typing import TypedDict
|
|
|
|
import orjson
|
|
from typing_extensions import override
|
|
|
|
from zerver.actions.channel_folders import check_add_channel_folder
|
|
from zerver.actions.realm_settings import (
|
|
do_change_realm_permission_group_setting,
|
|
do_set_realm_property,
|
|
)
|
|
from zerver.actions.streams import (
|
|
do_change_stream_group_based_setting,
|
|
do_change_stream_permission,
|
|
do_deactivate_stream,
|
|
)
|
|
from zerver.actions.user_groups import add_subgroups_to_user_group, check_add_user_group
|
|
from zerver.actions.users import do_change_user_role
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
from zerver.lib.test_helpers import get_subscription
|
|
from zerver.lib.types import UserGroupMembersData
|
|
from zerver.lib.user_groups import get_group_setting_value_for_api
|
|
from zerver.models import NamedUserGroup, Recipient, Stream, Subscription, UserProfile
|
|
from zerver.models.groups import SystemGroups
|
|
from zerver.models.realms import get_realm
|
|
from zerver.models.streams import StreamTopicsPolicyEnum, get_stream
|
|
|
|
|
|
class ChannelSubscriptionPermissionTest(ZulipTestCase):
|
|
@override
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
self.test_user = self.example_user("hamlet")
|
|
|
|
def test_realm_settings_for_subscribing_other_users(self) -> None:
|
|
"""
|
|
You can't subscribe other people to streams if you are a guest or your account is not old
|
|
enough.
|
|
"""
|
|
user_profile = self.example_user("cordelia")
|
|
invitee_user_id = user_profile.id
|
|
realm = user_profile.realm
|
|
|
|
admins_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", admins_group, acting_user=None
|
|
)
|
|
|
|
# User should be allowed to add subscribers when creating the
|
|
# channel even if they don't have realm wide permission to
|
|
# add other subscribers to a channel.
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MODERATOR, acting_user=None)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream1"],
|
|
# Creator will be part of `can_administer_channel_group` by
|
|
# default for a new channel. We set it to admin, so that we
|
|
# can test for errors in the next piece of this test.
|
|
{
|
|
"principals": orjson.dumps([invitee_user_id]).decode(),
|
|
"can_administer_channel_group": admins_group.id,
|
|
},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_success(result)
|
|
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream1"],
|
|
{"principals": orjson.dumps([self.example_user("aaron").id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
nobody_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.NOBODY, realm=realm, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", nobody_group, acting_user=None
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
get_stream("stream1", realm),
|
|
"can_add_subscribers_group",
|
|
nobody_group,
|
|
acting_user=user_profile,
|
|
)
|
|
# Admins have a special permission to administer every channel
|
|
# they have access to. This also grants them access to add
|
|
# subscribers.
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user, ["stream1"], {"principals": orjson.dumps([invitee_user_id]).decode()}
|
|
)
|
|
|
|
moderators_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", moderators_group, acting_user=None
|
|
)
|
|
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MEMBER, acting_user=None)
|
|
# Make sure that we are checking the permission with a full member,
|
|
# as full member is the user just below moderator in the role hierarchy.
|
|
self.assertFalse(self.test_user.is_provisional_member)
|
|
# User will be able to add subscribers to a newly created
|
|
# stream without any realm wide permissions. We create this
|
|
# stream programmatically so that we can test for errors for an
|
|
# existing stream.
|
|
self.make_stream("stream2")
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MODERATOR, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user, ["stream2"], {"principals": orjson.dumps([invitee_user_id]).decode()}
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
members_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.MEMBERS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", members_group, acting_user=None
|
|
)
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_GUEST, acting_user=None)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Not allowed for guest users")
|
|
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MEMBER, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([self.test_user.id, invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
full_members_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.FULL_MEMBERS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", full_members_group, acting_user=None
|
|
)
|
|
do_set_realm_property(realm, "waiting_period_threshold", 100000, acting_user=None)
|
|
self.assertTrue(user_profile.is_provisional_member)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
# Moderators, Admins and owners are always full members.
|
|
self.assertTrue(user_profile.is_provisional_member)
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MODERATOR, acting_user=None)
|
|
self.assertFalse(self.test_user.is_provisional_member)
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
|
|
self.assertFalse(self.test_user.is_provisional_member)
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_REALM_OWNER, acting_user=None)
|
|
self.assertFalse(self.test_user.is_provisional_member)
|
|
|
|
do_set_realm_property(realm, "waiting_period_threshold", 0, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user, ["stream2"], {"principals": orjson.dumps([invitee_user_id]).decode()}
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
named_user_group = check_add_user_group(
|
|
realm, "named_user_group", [self.test_user], acting_user=self.test_user
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm,
|
|
"can_add_subscribers_group",
|
|
named_user_group,
|
|
acting_user=None,
|
|
)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
anonymous_group = self.create_or_update_anonymous_group_for_setting([self.test_user], [])
|
|
|
|
do_change_realm_permission_group_setting(
|
|
realm,
|
|
"can_add_subscribers_group",
|
|
anonymous_group,
|
|
acting_user=None,
|
|
)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
def test_stream_settings_for_subscribing_other_users(self) -> None:
|
|
user_profile = self.example_user("cordelia")
|
|
invitee_user_id = user_profile.id
|
|
realm = user_profile.realm
|
|
|
|
nobody_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.NOBODY, realm=realm, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", nobody_group, acting_user=None
|
|
)
|
|
|
|
# User will be able to add subscribers to a newly created
|
|
# stream without any realm wide permissions. We create this
|
|
# stream programmatically so that we can test for errors for an
|
|
# existing stream.
|
|
do_change_stream_group_based_setting(
|
|
self.make_stream("stream1"),
|
|
"can_add_subscribers_group",
|
|
nobody_group,
|
|
acting_user=user_profile,
|
|
)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream1"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
# Admins have a special permission to administer every channel
|
|
# they have access to. This also grants them access to add
|
|
# subscribers.
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
|
|
result = self.subscribe_via_post(
|
|
self.test_user, ["stream1"], {"principals": orjson.dumps([invitee_user_id]).decode()}
|
|
)
|
|
self.assert_json_success(result)
|
|
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MEMBER, acting_user=None)
|
|
# Make sure that we are checking the permission with a full member,
|
|
# as full member is the user just below moderator in the role hierarchy.
|
|
self.assertFalse(self.test_user.is_provisional_member)
|
|
|
|
# User will be able to add subscribers to a newly created
|
|
# stream without any realm wide permissions. We create this
|
|
# stream programmatically so that we can test for errors for an
|
|
# existing stream.
|
|
stream2 = self.make_stream("stream2")
|
|
moderators_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.MODERATORS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream2, "can_add_subscribers_group", moderators_group, acting_user=user_profile
|
|
)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MODERATOR, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user, ["stream2"], {"principals": orjson.dumps([invitee_user_id]).decode()}
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
members_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.MEMBERS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream2, "can_add_subscribers_group", members_group, acting_user=user_profile
|
|
)
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_GUEST, acting_user=None)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Not allowed for guest users")
|
|
|
|
do_change_user_role(self.test_user, UserProfile.ROLE_MEMBER, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([self.test_user.id, invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
# User should be able to subscribe other users if they have
|
|
# permissions to administer the channel.
|
|
do_change_stream_group_based_setting(
|
|
stream2, "can_add_subscribers_group", nobody_group, acting_user=user_profile
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream2, "can_administer_channel_group", members_group, acting_user=user_profile
|
|
)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([self.test_user.id, invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
do_change_stream_group_based_setting(
|
|
stream2, "can_administer_channel_group", nobody_group, acting_user=user_profile
|
|
)
|
|
|
|
full_members_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.FULL_MEMBERS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream2, "can_add_subscribers_group", full_members_group, acting_user=user_profile
|
|
)
|
|
do_set_realm_property(realm, "waiting_period_threshold", 100000, acting_user=None)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
do_set_realm_property(realm, "waiting_period_threshold", 0, acting_user=None)
|
|
self.subscribe_via_post(
|
|
self.test_user, ["stream2"], {"principals": orjson.dumps([invitee_user_id]).decode()}
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
named_user_group = check_add_user_group(
|
|
realm, "named_user_group", [self.test_user], acting_user=self.test_user
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream2,
|
|
"can_add_subscribers_group",
|
|
named_user_group,
|
|
acting_user=user_profile,
|
|
)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
anonymous_group_member_dict = UserGroupMembersData(
|
|
direct_members=[self.test_user.id], direct_subgroups=[]
|
|
)
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream2,
|
|
"can_add_subscribers_group",
|
|
anonymous_group_member_dict,
|
|
acting_user=user_profile,
|
|
)
|
|
self.subscribe_via_post(
|
|
self.test_user,
|
|
["stream2"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
)
|
|
self.unsubscribe(user_profile, "stream2")
|
|
|
|
private_stream = self.make_stream("private_stream", invite_only=True)
|
|
do_change_stream_group_based_setting(
|
|
private_stream, "can_add_subscribers_group", members_group, acting_user=user_profile
|
|
)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["private_stream"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
)
|
|
self.assert_json_success(result)
|
|
do_change_stream_group_based_setting(
|
|
private_stream, "can_add_subscribers_group", nobody_group, acting_user=user_profile
|
|
)
|
|
self.unsubscribe(user_profile, "private_stream")
|
|
|
|
do_change_stream_group_based_setting(
|
|
private_stream,
|
|
"can_administer_channel_group",
|
|
members_group,
|
|
acting_user=user_profile,
|
|
)
|
|
result = self.subscribe_via_post(
|
|
self.test_user,
|
|
["private_stream"],
|
|
{"principals": orjson.dumps([invitee_user_id]).decode()},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Unable to access channel (private_stream).")
|
|
|
|
def test_stream_settings_for_subscribing(self) -> None:
|
|
realm = get_realm("zulip")
|
|
|
|
stream = self.make_stream("public_stream")
|
|
|
|
nobody_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.NOBODY, realm=realm, is_system_group=True
|
|
)
|
|
|
|
def check_user_can_subscribe(user: UserProfile, error_msg: str | None = None) -> None:
|
|
result = self.subscribe_via_post(
|
|
user,
|
|
[stream.name],
|
|
allow_fail=error_msg is not None,
|
|
)
|
|
if error_msg:
|
|
self.assert_json_error(result, error_msg)
|
|
return
|
|
|
|
self.assertTrue(
|
|
Subscription.objects.filter(
|
|
recipient__type=Recipient.STREAM,
|
|
recipient__type_id=stream.id,
|
|
user_profile=user,
|
|
).exists()
|
|
)
|
|
# Unsubscribe user again for testing next case.
|
|
self.unsubscribe(user, stream.name)
|
|
|
|
desdemona = self.example_user("desdemona")
|
|
shiva = self.example_user("shiva")
|
|
hamlet = self.example_user("hamlet")
|
|
polonius = self.example_user("polonius")
|
|
othello = self.example_user("othello")
|
|
|
|
do_change_realm_permission_group_setting(
|
|
realm, "can_add_subscribers_group", nobody_group, acting_user=othello
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", nobody_group, acting_user=othello
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", nobody_group, acting_user=othello
|
|
)
|
|
|
|
check_user_can_subscribe(desdemona)
|
|
check_user_can_subscribe(shiva)
|
|
check_user_can_subscribe(hamlet)
|
|
check_user_can_subscribe(othello)
|
|
check_user_can_subscribe(polonius, "Not allowed for guest users")
|
|
|
|
setting_group_member_dict = UserGroupMembersData(
|
|
direct_members=[polonius.id], direct_subgroups=[]
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", setting_group_member_dict, acting_user=othello
|
|
)
|
|
|
|
check_user_can_subscribe(polonius, "Not allowed for guest users")
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", nobody_group, acting_user=othello
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", setting_group_member_dict, acting_user=othello
|
|
)
|
|
|
|
check_user_can_subscribe(polonius, "Not allowed for guest users")
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", nobody_group, acting_user=othello
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", setting_group_member_dict, acting_user=othello
|
|
)
|
|
|
|
check_user_can_subscribe(polonius, "Not allowed for guest users")
|
|
|
|
stream = self.subscribe(self.example_user("iago"), "private_stream", invite_only=True)
|
|
|
|
check_user_can_subscribe(desdemona, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(shiva, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(hamlet, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(othello, f"Unable to access channel ({stream.name}).")
|
|
|
|
owners_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.OWNERS, realm=realm, is_system_group=True
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", owners_group, acting_user=othello
|
|
)
|
|
|
|
check_user_can_subscribe(shiva, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(hamlet, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(othello, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(desdemona)
|
|
|
|
hamletcharacters_group = NamedUserGroup.objects.get(name="hamletcharacters", realm=realm)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", hamletcharacters_group, acting_user=othello
|
|
)
|
|
check_user_can_subscribe(shiva, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(desdemona, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(othello, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(hamlet)
|
|
|
|
setting_group_member_dict = UserGroupMembersData(
|
|
direct_members=[othello.id], direct_subgroups=[owners_group.id]
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", setting_group_member_dict, acting_user=othello
|
|
)
|
|
check_user_can_subscribe(shiva, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(hamlet, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(othello)
|
|
check_user_can_subscribe(desdemona)
|
|
|
|
# Users can also subscribe if they are allowed to subscribe other users.
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", nobody_group, acting_user=othello
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", setting_group_member_dict, acting_user=othello
|
|
)
|
|
check_user_can_subscribe(shiva, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(hamlet, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(othello)
|
|
check_user_can_subscribe(desdemona)
|
|
|
|
# Users cannot subscribe if they belong to can_administer_channel_group but
|
|
# do not belong to any of can_subscribe_group and can_add_subscribers_group.
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", nobody_group, acting_user=othello
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", setting_group_member_dict, acting_user=othello
|
|
)
|
|
check_user_can_subscribe(shiva, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(hamlet, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(othello, f"Unable to access channel ({stream.name}).")
|
|
check_user_can_subscribe(desdemona, f"Unable to access channel ({stream.name}).")
|
|
|
|
def test_can_remove_subscribers_group(self) -> None:
|
|
realm = get_realm("zulip")
|
|
iago = self.example_user("iago")
|
|
leadership_group = check_add_user_group(
|
|
realm,
|
|
"leadership",
|
|
[iago, self.example_user("shiva")],
|
|
acting_user=iago,
|
|
)
|
|
hamlet = self.example_user("hamlet")
|
|
managers_group = check_add_user_group(realm, "managers", [hamlet], acting_user=hamlet)
|
|
add_subgroups_to_user_group(managers_group, [leadership_group], acting_user=None)
|
|
cordelia = self.example_user("cordelia")
|
|
othello = self.example_user("othello")
|
|
shiva = self.example_user("shiva")
|
|
|
|
public_stream = self.make_stream("public_stream")
|
|
|
|
def check_unsubscribing_user(
|
|
user: UserProfile,
|
|
can_remove_subscribers_group: NamedUserGroup | UserGroupMembersData,
|
|
expect_fail: bool = False,
|
|
stream_list: list[Stream] | None = None,
|
|
skip_changing_group_setting: bool = False,
|
|
) -> None:
|
|
self.login_user(user)
|
|
if stream_list is None:
|
|
stream_list = [public_stream]
|
|
for stream in stream_list:
|
|
self.subscribe(cordelia, stream.name)
|
|
if not skip_changing_group_setting:
|
|
do_change_stream_group_based_setting(
|
|
stream,
|
|
"can_remove_subscribers_group",
|
|
can_remove_subscribers_group,
|
|
acting_user=user,
|
|
)
|
|
stream_name_list = [stream.name for stream in stream_list]
|
|
result = self.client_delete(
|
|
"/json/users/me/subscriptions",
|
|
{
|
|
"subscriptions": orjson.dumps(stream_name_list).decode(),
|
|
"principals": orjson.dumps([cordelia.id]).decode(),
|
|
},
|
|
)
|
|
if expect_fail:
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
return
|
|
|
|
json = self.assert_json_success(result)
|
|
self.assert_length(json["removed"], len(stream_name_list))
|
|
self.assert_length(json["not_removed"], 0)
|
|
|
|
check_unsubscribing_user(
|
|
self.example_user("hamlet"),
|
|
leadership_group,
|
|
expect_fail=True,
|
|
stream_list=[public_stream],
|
|
)
|
|
check_unsubscribing_user(iago, leadership_group, stream_list=[public_stream])
|
|
# Owners can unsubscribe others when they are not a member of
|
|
# the allowed group since owners have the permission to
|
|
# administer all channels.
|
|
check_unsubscribing_user(
|
|
self.example_user("desdemona"), leadership_group, stream_list=[public_stream]
|
|
)
|
|
|
|
check_unsubscribing_user(
|
|
othello,
|
|
managers_group,
|
|
expect_fail=True,
|
|
stream_list=[public_stream],
|
|
)
|
|
check_unsubscribing_user(shiva, managers_group, stream_list=[public_stream])
|
|
check_unsubscribing_user(hamlet, managers_group, stream_list=[public_stream])
|
|
|
|
private_stream = self.make_stream("private_stream", invite_only=True)
|
|
self.subscribe(self.example_user("hamlet"), private_stream.name)
|
|
# Users are not allowed to unsubscribe others from streams they
|
|
# don't have metadata access to even if they are a member of the
|
|
# allowed group. In this case, a non-admin who is not subscribed
|
|
# to the channel does not have metadata access to the channel.
|
|
check_unsubscribing_user(
|
|
shiva,
|
|
leadership_group,
|
|
expect_fail=True,
|
|
stream_list=[private_stream],
|
|
)
|
|
check_unsubscribing_user(iago, leadership_group, stream_list=[private_stream])
|
|
# Users are allowed to unsubscribe others from private streams
|
|
# they have access to if they are a member of the allowed
|
|
# group. In this case, a user with the role `owner` is
|
|
# subscribed to the relevant channel.
|
|
check_unsubscribing_user(
|
|
self.example_user("desdemona"), leadership_group, stream_list=[private_stream]
|
|
)
|
|
self.subscribe(shiva, private_stream.name)
|
|
check_unsubscribing_user(shiva, leadership_group, stream_list=[private_stream])
|
|
|
|
# Test changing setting to anonymous group.
|
|
setting_group_member_dict = UserGroupMembersData(
|
|
direct_members=[hamlet.id],
|
|
direct_subgroups=[leadership_group.id],
|
|
)
|
|
check_unsubscribing_user(
|
|
othello,
|
|
setting_group_member_dict,
|
|
expect_fail=True,
|
|
stream_list=[private_stream],
|
|
)
|
|
check_unsubscribing_user(hamlet, setting_group_member_dict, stream_list=[private_stream])
|
|
check_unsubscribing_user(iago, setting_group_member_dict, stream_list=[private_stream])
|
|
check_unsubscribing_user(shiva, setting_group_member_dict, stream_list=[private_stream])
|
|
|
|
# Owners can unsubscribe others when they are not a member of
|
|
# the allowed group since admins have the permission to
|
|
# administer all channels.
|
|
setting_group_member_dict = UserGroupMembersData(
|
|
direct_members=[hamlet.id],
|
|
direct_subgroups=[],
|
|
)
|
|
check_unsubscribing_user(
|
|
self.example_user("desdemona"), setting_group_member_dict, stream_list=[private_stream]
|
|
)
|
|
check_unsubscribing_user(iago, setting_group_member_dict, stream_list=[private_stream])
|
|
|
|
# A user who is part of can_administer_channel_group should be
|
|
# able to unsubscribe other users even if that user is not part
|
|
# of can_remove_subscribers_group. And even if that user is not
|
|
# subscribed to the channel in question.
|
|
with self.assertRaises(Subscription.DoesNotExist):
|
|
get_subscription(private_stream.name, othello)
|
|
check_unsubscribing_user(othello, setting_group_member_dict, expect_fail=True)
|
|
othello_group_member_dict = UserGroupMembersData(
|
|
direct_members=[othello.id], direct_subgroups=[]
|
|
)
|
|
private_stream_2 = self.make_stream("private_stream_2")
|
|
do_change_stream_group_based_setting(
|
|
private_stream,
|
|
"can_administer_channel_group",
|
|
othello_group_member_dict,
|
|
acting_user=othello,
|
|
)
|
|
# If the user can only administer one of the channels, the test
|
|
# should fail.
|
|
check_unsubscribing_user(
|
|
othello,
|
|
setting_group_member_dict,
|
|
expect_fail=True,
|
|
stream_list=[private_stream, private_stream_2],
|
|
)
|
|
# User can administer both channels now.
|
|
do_change_stream_group_based_setting(
|
|
private_stream_2,
|
|
"can_administer_channel_group",
|
|
othello_group_member_dict,
|
|
acting_user=othello,
|
|
)
|
|
check_unsubscribing_user(
|
|
othello, setting_group_member_dict, stream_list=[private_stream, private_stream_2]
|
|
)
|
|
|
|
shiva_group_member_dict = UserGroupMembersData(
|
|
direct_members=[shiva.id], direct_subgroups=[]
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
private_stream,
|
|
"can_remove_subscribers_group",
|
|
shiva_group_member_dict,
|
|
acting_user=shiva,
|
|
)
|
|
self.subscribe(shiva, private_stream.name)
|
|
self.subscribe(shiva, private_stream_2.name)
|
|
# If the user can is present in the remove subscribers group of
|
|
# only one of the channels, the test should fail.
|
|
check_unsubscribing_user(
|
|
shiva,
|
|
setting_group_member_dict,
|
|
expect_fail=True,
|
|
stream_list=[private_stream, private_stream_2],
|
|
skip_changing_group_setting=True,
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
private_stream_2,
|
|
"can_remove_subscribers_group",
|
|
shiva_group_member_dict,
|
|
acting_user=shiva,
|
|
)
|
|
check_unsubscribing_user(
|
|
shiva,
|
|
setting_group_member_dict,
|
|
stream_list=[private_stream, private_stream_2],
|
|
skip_changing_group_setting=True,
|
|
)
|
|
|
|
def test_change_stream_message_retention_days_requires_realm_owner(self) -> None:
|
|
user_profile = self.example_user("iago")
|
|
self.login_user(user_profile)
|
|
realm = user_profile.realm
|
|
stream = self.subscribe(user_profile, "stream_name1")
|
|
|
|
result = self.client_patch(
|
|
f"/json/streams/{stream.id}", {"message_retention_days": orjson.dumps(2).decode()}
|
|
)
|
|
self.assert_json_error(result, "Must be an organization owner")
|
|
|
|
do_change_user_role(user_profile, UserProfile.ROLE_REALM_OWNER, acting_user=None)
|
|
result = self.client_patch(
|
|
f"/json/streams/{stream.id}", {"message_retention_days": orjson.dumps(2).decode()}
|
|
)
|
|
self.assert_json_success(result)
|
|
stream = get_stream("stream_name1", realm)
|
|
self.assertEqual(stream.message_retention_days, 2)
|
|
|
|
|
|
class PermissionCheckConfigDict(TypedDict):
|
|
setting_group: NamedUserGroup | UserGroupMembersData
|
|
users_with_permission: list[UserProfile]
|
|
users_without_permission: list[UserProfile]
|
|
|
|
|
|
class ChannelAdministerPermissionTest(ZulipTestCase):
|
|
@override
|
|
def setUp(self) -> None:
|
|
super().setUp()
|
|
self.realm = get_realm("zulip")
|
|
self.admin = self.example_user("iago")
|
|
self.moderator = self.example_user("shiva")
|
|
self.guest = self.example_user("polonius")
|
|
|
|
self.hamletcharacters_group = NamedUserGroup.objects.get(
|
|
name="hamletcharacters", realm=self.realm
|
|
)
|
|
self.moderators_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.MODERATORS, realm=self.realm, is_system_group=True
|
|
)
|
|
self.nobody_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.NOBODY, realm=self.realm, is_system_group=True
|
|
)
|
|
self.members_group = NamedUserGroup.objects.get(
|
|
name=SystemGroups.MEMBERS, realm=self.realm, is_system_group=True
|
|
)
|
|
|
|
def do_test_updating_channel(
|
|
self, stream: Stream, property_name: str, new_value: str | int | bool
|
|
) -> None:
|
|
hamlet = self.example_user("hamlet")
|
|
prospero = self.example_user("prospero")
|
|
|
|
# For some properties, name of the field in Stream model
|
|
# is different from parameter name used in API request.
|
|
api_parameter_name_dict = dict(
|
|
name="new_name",
|
|
deactivated="is_archived",
|
|
)
|
|
api_parameter_name = property_name
|
|
if property_name in api_parameter_name_dict:
|
|
api_parameter_name = api_parameter_name_dict[property_name]
|
|
|
|
data = {}
|
|
if property_name == "topics_policy":
|
|
data[api_parameter_name] = StreamTopicsPolicyEnum(new_value).name
|
|
elif not isinstance(new_value, str):
|
|
data[api_parameter_name] = orjson.dumps(new_value).decode()
|
|
else:
|
|
data[api_parameter_name] = new_value
|
|
|
|
default_error_msg = "You do not have permission to administer this channel."
|
|
|
|
def check_channel_property_update(user: UserProfile, error_msg: str | None = None) -> None:
|
|
old_value = getattr(stream, property_name)
|
|
|
|
if property_name == "deactivated" and new_value is True:
|
|
# There is a separate endpoint for deactivating streams.
|
|
result = self.api_delete(user, f"/api/v1/streams/{stream.id}")
|
|
else:
|
|
result = self.api_patch(user, f"/api/v1/streams/{stream.id}", info=data)
|
|
|
|
if error_msg is not None:
|
|
self.assert_json_error(result, error_msg)
|
|
return
|
|
|
|
self.assert_json_success(result)
|
|
stream.refresh_from_db()
|
|
self.assertEqual(getattr(stream, property_name), new_value)
|
|
|
|
# Reset to original value.
|
|
setattr(stream, property_name, old_value)
|
|
stream.save(update_fields=[property_name])
|
|
|
|
anonymous_group_dict = UserGroupMembersData(
|
|
direct_members=[prospero.id, self.guest.id], direct_subgroups=[]
|
|
)
|
|
|
|
group_permission_checks: list[PermissionCheckConfigDict] = [
|
|
# Check admin can always administer channel.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.nobody_group,
|
|
users_without_permission=[self.moderator],
|
|
users_with_permission=[self.admin],
|
|
),
|
|
# Check case when can_administer_channel_group is set to a system group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.moderators_group,
|
|
users_without_permission=[hamlet],
|
|
users_with_permission=[self.moderator],
|
|
),
|
|
# Check case when can_administer_channel_group is set to a user-defined group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.hamletcharacters_group,
|
|
users_without_permission=[self.moderator],
|
|
users_with_permission=[hamlet],
|
|
),
|
|
# Check case when can_administer_channel_group is set to an anonymous group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=anonymous_group_dict,
|
|
users_without_permission=[self.moderator, hamlet],
|
|
users_with_permission=[prospero],
|
|
),
|
|
]
|
|
|
|
for check_config in group_permission_checks:
|
|
do_change_stream_group_based_setting(
|
|
stream,
|
|
"can_administer_channel_group",
|
|
check_config["setting_group"],
|
|
acting_user=self.admin,
|
|
)
|
|
|
|
for user in check_config["users_without_permission"]:
|
|
error_msg = default_error_msg
|
|
check_channel_property_update(user, error_msg=error_msg)
|
|
|
|
for user in check_config["users_with_permission"]:
|
|
check_channel_property_update(user)
|
|
|
|
# Check guests cannot update property even when they belong
|
|
# to "can_administer_channel_group".
|
|
check_channel_property_update(self.guest, error_msg="Invalid channel ID")
|
|
self.subscribe(self.guest, stream.name)
|
|
check_channel_property_update(self.guest, error_msg=default_error_msg)
|
|
self.unsubscribe(self.guest, stream.name)
|
|
|
|
# Check for permission in unsubscribed private streams.
|
|
if stream.invite_only:
|
|
self.unsubscribe(self.admin, stream.name)
|
|
self.unsubscribe(prospero, stream.name)
|
|
self.unsubscribe(hamlet, stream.name)
|
|
|
|
# Hamlet does not have metadata access.
|
|
check_channel_property_update(hamlet, error_msg="Invalid channel ID")
|
|
# Admins always have metadata access and administering permission.
|
|
check_channel_property_update(self.admin)
|
|
# Prospero has metadata access by being in can_administer_channel_group.
|
|
check_channel_property_update(prospero)
|
|
|
|
# Re-subscribe users for next tests.
|
|
self.subscribe(self.admin, stream.name)
|
|
self.subscribe(prospero, stream.name)
|
|
self.subscribe(hamlet, stream.name)
|
|
|
|
def test_administering_permission_for_updating_channel(self) -> None:
|
|
"""
|
|
This test is only for checking permission to update basic channel
|
|
properties like name, description, folder and permission to unarchive
|
|
the channel. Other things like permission to update group settings and
|
|
channel privacy are tested separately.
|
|
"""
|
|
public_stream = self.make_stream("test stream")
|
|
|
|
private_stream = self.make_stream("private_stream", invite_only=True)
|
|
self.subscribe(self.admin, private_stream.name)
|
|
self.subscribe(self.moderator, private_stream.name)
|
|
self.subscribe(self.example_user("hamlet"), private_stream.name)
|
|
self.subscribe(self.example_user("prospero"), private_stream.name)
|
|
|
|
channel_folder = check_add_channel_folder(
|
|
self.realm, "Frontend", "", acting_user=self.admin
|
|
)
|
|
|
|
for stream in [public_stream, private_stream]:
|
|
self.do_test_updating_channel(stream, "name", "Renamed stream")
|
|
self.do_test_updating_channel(stream, "description", "Edited stream description")
|
|
self.do_test_updating_channel(stream, "folder_id", channel_folder.id)
|
|
self.do_test_updating_channel(
|
|
stream, "topics_policy", StreamTopicsPolicyEnum.allow_empty_topic.value
|
|
)
|
|
self.do_test_updating_channel(stream, "deactivated", True)
|
|
|
|
do_deactivate_stream(stream, acting_user=None)
|
|
self.do_test_updating_channel(stream, "deactivated", False)
|
|
|
|
def check_channel_privacy_update(
|
|
self, user: UserProfile, property_name: str, new_value: bool, error_msg: str | None = None
|
|
) -> None:
|
|
stream = get_stream("test_stream", user.realm)
|
|
data = {}
|
|
|
|
old_values = {
|
|
"invite_only": stream.invite_only,
|
|
"is_web_public": stream.is_web_public,
|
|
"history_public_to_subscribers": stream.history_public_to_subscribers,
|
|
}
|
|
|
|
if property_name == "invite_only":
|
|
data["is_private"] = orjson.dumps(new_value).decode()
|
|
else:
|
|
data[property_name] = orjson.dumps(new_value).decode()
|
|
|
|
result = self.api_patch(user, f"/api/v1/streams/{stream.id}", info=data)
|
|
|
|
if error_msg is not None:
|
|
self.assert_json_error(result, error_msg)
|
|
return
|
|
|
|
self.assert_json_success(result)
|
|
stream.refresh_from_db()
|
|
self.assertEqual(getattr(stream, property_name), new_value)
|
|
|
|
# Reset to original value.
|
|
do_change_stream_permission(
|
|
stream,
|
|
**old_values,
|
|
acting_user=self.admin,
|
|
)
|
|
|
|
def do_test_updating_channel_privacy(self, property_name: str, new_value: bool) -> None:
|
|
hamlet = self.example_user("hamlet")
|
|
prospero = self.example_user("prospero")
|
|
|
|
stream = get_stream("test_stream", self.realm)
|
|
|
|
data = {}
|
|
if property_name == "invite_only":
|
|
data["is_private"] = orjson.dumps(new_value).decode()
|
|
else:
|
|
data[property_name] = orjson.dumps(new_value).decode()
|
|
|
|
default_error_msg = "You do not have permission to administer this channel."
|
|
|
|
anonymous_group_dict = UserGroupMembersData(
|
|
direct_members=[prospero.id, self.guest.id], direct_subgroups=[]
|
|
)
|
|
|
|
group_permission_checks: list[PermissionCheckConfigDict] = [
|
|
# Check admin can always administer channel.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.nobody_group,
|
|
users_without_permission=[self.moderator],
|
|
users_with_permission=[self.admin],
|
|
),
|
|
# Check case when can_administer_channel_group is set to a system group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.moderators_group,
|
|
users_without_permission=[hamlet],
|
|
users_with_permission=[self.moderator],
|
|
),
|
|
# Check case when can_administer_channel_group is set to a user-defined group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.hamletcharacters_group,
|
|
users_without_permission=[self.moderator],
|
|
users_with_permission=[hamlet],
|
|
),
|
|
# Check case when can_administer_channel_group is set to an anonymous group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=anonymous_group_dict,
|
|
users_without_permission=[self.moderator, hamlet],
|
|
users_with_permission=[prospero],
|
|
),
|
|
]
|
|
|
|
for check_config in group_permission_checks:
|
|
do_change_stream_group_based_setting(
|
|
stream,
|
|
"can_administer_channel_group",
|
|
check_config["setting_group"],
|
|
acting_user=self.admin,
|
|
)
|
|
|
|
for user in check_config["users_without_permission"]:
|
|
self.check_channel_privacy_update(user, property_name, new_value, default_error_msg)
|
|
|
|
for user in check_config["users_with_permission"]:
|
|
self.check_channel_privacy_update(user, property_name, new_value)
|
|
|
|
# Check guests cannot update property even when they belong
|
|
# to "can_administer_channel_group".
|
|
self.check_channel_privacy_update(
|
|
self.guest, property_name, new_value, error_msg="Invalid channel ID"
|
|
)
|
|
self.subscribe(self.guest, stream.name)
|
|
self.check_channel_privacy_update(self.guest, property_name, new_value, default_error_msg)
|
|
self.unsubscribe(self.guest, stream.name)
|
|
|
|
def test_administering_permission_for_updating_channel_privacy(self) -> None:
|
|
stream = self.make_stream("test_stream")
|
|
|
|
# Give permission to create web-public channels to everyone, so
|
|
# that we can check administering permissions easily, although
|
|
# we do not do not allow members to create web-public channels
|
|
# in production.
|
|
do_change_realm_permission_group_setting(
|
|
self.realm, "can_create_web_public_channel_group", self.members_group, acting_user=None
|
|
)
|
|
|
|
# Test making a public stream private with protected history.
|
|
self.do_test_updating_channel_privacy("invite_only", True)
|
|
|
|
# Test making a public stream web-public.
|
|
self.do_test_updating_channel_privacy("is_web_public", True)
|
|
|
|
do_change_stream_permission(
|
|
stream,
|
|
invite_only=True,
|
|
history_public_to_subscribers=False,
|
|
is_web_public=False,
|
|
acting_user=self.admin,
|
|
)
|
|
self.subscribe(self.admin, stream.name)
|
|
self.subscribe(self.moderator, stream.name)
|
|
self.subscribe(self.example_user("hamlet"), stream.name)
|
|
self.subscribe(self.example_user("prospero"), stream.name)
|
|
|
|
# Test making a private stream with protected history public.
|
|
self.do_test_updating_channel_privacy("invite_only", False)
|
|
|
|
def test_permission_for_updating_privacy_of_unsubscribed_private_channel(self) -> None:
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
stream = self.make_stream("test_stream", invite_only=True)
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", self.members_group, acting_user=self.admin
|
|
)
|
|
|
|
error_msg = "Channel content access is required."
|
|
self.check_channel_privacy_update(self.admin, "invite_only", False, error_msg)
|
|
self.check_channel_privacy_update(self.moderator, "invite_only", False, error_msg)
|
|
self.check_channel_privacy_update(hamlet, "invite_only", False, error_msg)
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", self.moderators_group, acting_user=self.admin
|
|
)
|
|
self.check_channel_privacy_update(hamlet, "invite_only", False, error_msg=error_msg)
|
|
self.check_channel_privacy_update(self.admin, "invite_only", False)
|
|
self.check_channel_privacy_update(self.moderator, "invite_only", False)
|
|
|
|
# Users who are part of can_subscribe_group get content access
|
|
# to a private stream even if they are not subscribed to it.
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", self.hamletcharacters_group, acting_user=self.admin
|
|
)
|
|
self.check_channel_privacy_update(hamlet, "invite_only", False)
|
|
|
|
def check_channel_group_setting_update(
|
|
self, user: UserProfile, property_name: str, error_msg: str | None = None
|
|
) -> None:
|
|
stream = get_stream("test_stream", user.realm)
|
|
new_value = self.hamletcharacters_group.id
|
|
|
|
data = {}
|
|
data[property_name] = orjson.dumps({"new": new_value}).decode()
|
|
|
|
old_value = getattr(stream, property_name)
|
|
# old_value is stored as UserGroupMembersData dict if the
|
|
# setting is set to an anonymous group and a NamedUserGroup
|
|
# object otherwise, so that we can pass it directly to
|
|
# do_change_stream_group_based_setting.
|
|
if not hasattr(old_value, "named_user_group"):
|
|
old_value = get_group_setting_value_for_api(old_value)
|
|
else:
|
|
old_value = old_value.named_user_group
|
|
|
|
result = self.api_patch(user, f"/api/v1/streams/{stream.id}", info=data)
|
|
|
|
if error_msg is not None:
|
|
self.assert_json_error(result, error_msg)
|
|
return
|
|
|
|
self.assert_json_success(result)
|
|
stream.refresh_from_db()
|
|
self.assertEqual(getattr(stream, property_name + "_id"), new_value)
|
|
|
|
# Reset to original value.
|
|
do_change_stream_group_based_setting(
|
|
stream, property_name, old_value, acting_user=self.example_user("iago")
|
|
)
|
|
|
|
def do_test_updating_channel_group_settings(self, property_name: str) -> None:
|
|
hamlet = self.example_user("hamlet")
|
|
prospero = self.example_user("prospero")
|
|
|
|
stream = get_stream("test_stream", self.realm)
|
|
|
|
default_error_msg = "You do not have permission to administer this channel."
|
|
|
|
anonymous_group_dict = UserGroupMembersData(
|
|
direct_members=[prospero.id, self.guest.id], direct_subgroups=[]
|
|
)
|
|
|
|
group_permission_checks: list[PermissionCheckConfigDict] = [
|
|
# Check admin can always administer channel.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.nobody_group,
|
|
users_without_permission=[self.moderator],
|
|
users_with_permission=[self.admin],
|
|
),
|
|
# Check case when can_administer_channel_group is set to a system group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.moderators_group,
|
|
users_without_permission=[hamlet],
|
|
users_with_permission=[self.moderator],
|
|
),
|
|
# Check case when can_administer_channel_group is set to a user-defined group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=self.hamletcharacters_group,
|
|
users_without_permission=[self.moderator],
|
|
users_with_permission=[hamlet],
|
|
),
|
|
# Check case when can_administer_channel_group is set to an anonymous group.
|
|
PermissionCheckConfigDict(
|
|
setting_group=anonymous_group_dict,
|
|
users_without_permission=[self.moderator, hamlet],
|
|
users_with_permission=[prospero],
|
|
),
|
|
]
|
|
|
|
for check_config in group_permission_checks:
|
|
do_change_stream_group_based_setting(
|
|
stream,
|
|
"can_administer_channel_group",
|
|
check_config["setting_group"],
|
|
acting_user=self.admin,
|
|
)
|
|
|
|
for user in check_config["users_without_permission"]:
|
|
self.check_channel_group_setting_update(user, property_name, default_error_msg)
|
|
|
|
for user in check_config["users_with_permission"]:
|
|
self.check_channel_group_setting_update(user, property_name)
|
|
|
|
# Check guests cannot update property even when they belong
|
|
# to "can_administer_channel_group".
|
|
self.check_channel_group_setting_update(
|
|
self.guest, property_name, error_msg="Invalid channel ID"
|
|
)
|
|
self.subscribe(self.guest, stream.name)
|
|
self.check_channel_group_setting_update(self.guest, property_name, default_error_msg)
|
|
self.unsubscribe(self.guest, stream.name)
|
|
|
|
def do_test_updating_group_settings_for_unsubscribed_private_channels(
|
|
self, property_name: str
|
|
) -> None:
|
|
# If stream is private, test which permissions require having
|
|
# content access to the channel.
|
|
hamlet = self.example_user("hamlet")
|
|
|
|
stream = get_stream("test_stream", self.realm)
|
|
self.assertTrue(stream.invite_only)
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", self.members_group, acting_user=self.admin
|
|
)
|
|
|
|
if property_name not in Stream.stream_permission_group_settings_requiring_content_access:
|
|
# Users without content access can modify properties not in
|
|
# stream_permission_group_settings_requiring_content_access.
|
|
self.check_channel_group_setting_update(self.admin, property_name)
|
|
self.check_channel_group_setting_update(self.moderator, property_name)
|
|
self.check_channel_group_setting_update(hamlet, property_name)
|
|
return
|
|
|
|
error_msg = "Channel content access is required."
|
|
# Even realm and channel admins need content access to
|
|
# a private channel to update the permissions in
|
|
# stream_permission_group_settings_requiring_content_access.
|
|
self.check_channel_group_setting_update(self.admin, property_name, error_msg=error_msg)
|
|
self.check_channel_group_setting_update(self.moderator, property_name, error_msg=error_msg)
|
|
self.check_channel_group_setting_update(hamlet, property_name, error_msg=error_msg)
|
|
|
|
# Users who are part of can_add_subscribers_group get content access
|
|
# to a private stream even if they are not subscribed to it.
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", self.moderators_group, acting_user=self.admin
|
|
)
|
|
self.check_channel_group_setting_update(hamlet, property_name, error_msg=error_msg)
|
|
self.check_channel_group_setting_update(self.admin, property_name)
|
|
self.check_channel_group_setting_update(self.moderator, property_name)
|
|
|
|
# Users who are part of can_subscribe_group get content access
|
|
# to a private stream even if they are not subscribed to it.
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", self.hamletcharacters_group, acting_user=self.admin
|
|
)
|
|
self.check_channel_group_setting_update(hamlet, property_name)
|
|
|
|
# Reset the setting values to "Nobody" group.
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_add_subscribers_group", self.nobody_group, acting_user=self.admin
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_subscribe_group", self.nobody_group, acting_user=self.admin
|
|
)
|
|
|
|
def test_administering_permission_for_updating_channel_group_settings(self) -> None:
|
|
stream = self.make_stream("test_stream")
|
|
hamlet = self.example_user("hamlet")
|
|
prospero = self.example_user("prospero")
|
|
|
|
do_change_realm_permission_group_setting(
|
|
hamlet.realm,
|
|
"can_set_delete_message_policy_group",
|
|
self.members_group,
|
|
acting_user=None,
|
|
)
|
|
|
|
for setting_name in Stream.stream_permission_group_settings:
|
|
self.do_test_updating_channel_group_settings(setting_name)
|
|
|
|
# Test changing group settings for a private stream when user is
|
|
# subscribed to the stream.
|
|
do_change_stream_permission(
|
|
stream,
|
|
invite_only=True,
|
|
history_public_to_subscribers=False,
|
|
is_web_public=False,
|
|
acting_user=self.admin,
|
|
)
|
|
for user in [self.admin, self.moderator, hamlet, prospero]:
|
|
self.subscribe(user, stream.name)
|
|
|
|
for setting_name in Stream.stream_permission_group_settings:
|
|
self.do_test_updating_channel_group_settings(setting_name)
|
|
|
|
# Unsubscribe user from private stream to test gaining
|
|
# content access from group settings.
|
|
for user in [self.admin, self.moderator, hamlet, prospero]:
|
|
self.unsubscribe(user, stream.name)
|
|
|
|
for setting_name in Stream.stream_permission_group_settings:
|
|
self.do_test_updating_group_settings_for_unsubscribed_private_channels(setting_name)
|
|
|
|
def test_realm_permission_to_update_topics_policy(self) -> None:
|
|
hamlet = self.example_user("hamlet")
|
|
stream = self.make_stream("test_stream")
|
|
|
|
def check_channel_topics_policy_update(user: UserProfile, allow_fail: bool = False) -> None:
|
|
data = {}
|
|
data["topics_policy"] = StreamTopicsPolicyEnum.allow_empty_topic.name
|
|
|
|
result = self.api_patch(user, f"/api/v1/streams/{stream.id}", info=data)
|
|
|
|
if allow_fail:
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
return
|
|
|
|
self.assert_json_success(result)
|
|
stream.refresh_from_db()
|
|
self.assertEqual(stream.topics_policy, StreamTopicsPolicyEnum.allow_empty_topic.value)
|
|
|
|
# Reset to original value
|
|
stream.topics_policy = StreamTopicsPolicyEnum.inherit.value
|
|
stream.save()
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", self.nobody_group, acting_user=self.admin
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
self.realm, "can_set_topics_policy_group", self.nobody_group, acting_user=None
|
|
)
|
|
|
|
# Admins can always update topics_policy.
|
|
check_channel_topics_policy_update(self.admin)
|
|
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", self.members_group, acting_user=self.admin
|
|
)
|
|
|
|
check_channel_topics_policy_update(self.moderator, allow_fail=True)
|
|
check_channel_topics_policy_update(hamlet, allow_fail=True)
|
|
|
|
# Test when can_set_topics_policy_group is set to a user-defined group.
|
|
do_change_realm_permission_group_setting(
|
|
self.realm, "can_set_topics_policy_group", self.hamletcharacters_group, acting_user=None
|
|
)
|
|
check_channel_topics_policy_update(self.admin)
|
|
check_channel_topics_policy_update(self.moderator, allow_fail=True)
|
|
check_channel_topics_policy_update(hamlet)
|
|
|
|
# Test when can_set_topics_policy_group is set to an anonymous group.
|
|
anonymous_group = self.create_or_update_anonymous_group_for_setting(
|
|
direct_members=[hamlet], direct_subgroups=[self.moderators_group]
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
self.realm, "can_set_topics_policy_group", anonymous_group, acting_user=None
|
|
)
|
|
check_channel_topics_policy_update(self.admin)
|
|
check_channel_topics_policy_update(self.moderator)
|
|
check_channel_topics_policy_update(hamlet)
|
|
|
|
def test_can_set_delete_message_policy_group(self) -> None:
|
|
user = self.example_user("hamlet")
|
|
iago = self.example_user("iago")
|
|
realm = user.realm
|
|
stream = get_stream("Verona", realm)
|
|
owners_system_group = NamedUserGroup.objects.get(
|
|
realm=realm, name=SystemGroups.OWNERS, is_system_group=True
|
|
)
|
|
moderators_system_group = NamedUserGroup.objects.get(
|
|
realm=realm, name=SystemGroups.MODERATORS, is_system_group=True
|
|
)
|
|
members_system_group = NamedUserGroup.objects.get(
|
|
realm=realm, name=SystemGroups.MEMBERS, is_system_group=True
|
|
)
|
|
do_change_realm_permission_group_setting(
|
|
realm,
|
|
"can_set_delete_message_policy_group",
|
|
moderators_system_group,
|
|
acting_user=None,
|
|
)
|
|
do_change_stream_group_based_setting(
|
|
stream, "can_administer_channel_group", members_system_group, acting_user=iago
|
|
)
|
|
# Only moderators can change channel-level delete permissions.
|
|
# Hamlet is not a moderator.
|
|
subscriptions = [{"name": "new_test_stream"}]
|
|
result = self.subscribe_via_post(
|
|
user,
|
|
subscriptions,
|
|
subdomain="zulip",
|
|
extra_post_data={
|
|
"can_delete_any_message_group": orjson.dumps(owners_system_group.id).decode()
|
|
},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
result = self.subscribe_via_post(
|
|
user,
|
|
subscriptions,
|
|
subdomain="zulip",
|
|
extra_post_data={
|
|
"can_delete_own_message_group": orjson.dumps(owners_system_group.id).decode()
|
|
},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
self.login("hamlet")
|
|
result = self.client_patch(
|
|
f"/json/streams/{stream.id}",
|
|
{
|
|
"can_delete_any_message_group": orjson.dumps(
|
|
{
|
|
"new": {
|
|
"direct_members": [user.id],
|
|
"direct_subgroups": [
|
|
owners_system_group.id,
|
|
moderators_system_group.id,
|
|
],
|
|
}
|
|
}
|
|
).decode(),
|
|
"can_delete_own_message_group": orjson.dumps(
|
|
{
|
|
"new": {
|
|
"direct_members": [user.id],
|
|
"direct_subgroups": [
|
|
owners_system_group.id,
|
|
moderators_system_group.id,
|
|
],
|
|
}
|
|
}
|
|
).decode(),
|
|
},
|
|
)
|
|
self.assert_json_error(result, "Insufficient permission")
|
|
|
|
moderator = self.example_user("shiva")
|
|
|
|
# Shiva is a moderator.
|
|
result = self.subscribe_via_post(
|
|
moderator,
|
|
subscriptions,
|
|
subdomain="zulip",
|
|
extra_post_data={
|
|
"can_delete_any_message_group": orjson.dumps(owners_system_group.id).decode()
|
|
},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_success(result)
|
|
|
|
result = self.subscribe_via_post(
|
|
moderator,
|
|
subscriptions,
|
|
subdomain="zulip",
|
|
extra_post_data={
|
|
"can_delete_own_message_group": orjson.dumps(owners_system_group.id).decode()
|
|
},
|
|
allow_fail=True,
|
|
)
|
|
self.assert_json_success(result)
|
|
|
|
self.login("shiva")
|
|
result = self.client_patch(
|
|
f"/json/streams/{stream.id}",
|
|
{
|
|
"can_delete_any_message_group": orjson.dumps(
|
|
{
|
|
"new": {
|
|
"direct_members": [user.id],
|
|
"direct_subgroups": [
|
|
owners_system_group.id,
|
|
moderators_system_group.id,
|
|
],
|
|
}
|
|
}
|
|
).decode(),
|
|
"can_delete_own_message_group": orjson.dumps(
|
|
{
|
|
"new": {
|
|
"direct_members": [user.id],
|
|
"direct_subgroups": [
|
|
owners_system_group.id,
|
|
moderators_system_group.id,
|
|
],
|
|
}
|
|
}
|
|
).decode(),
|
|
},
|
|
)
|
|
self.assert_json_success(result)
|