mirror of
https://github.com/zulip/zulip.git
synced 2025-11-15 19:31:58 +00:00
streams: Allow admins to unsubscribe others irrespective of setting.
This commit is contained in:
@@ -565,6 +565,10 @@ export function can_unsubscribe_others(sub: StreamSubscription): boolean {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current_user.is_admin) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return user_groups.is_user_in_setting_group(
|
return user_groups.is_user_in_setting_group(
|
||||||
sub.can_remove_subscribers_group,
|
sub.can_remove_subscribers_group,
|
||||||
people.my_current_user_id(),
|
people.my_current_user_id(),
|
||||||
|
|||||||
@@ -1154,8 +1154,15 @@ test("can_unsubscribe_others", ({override}) => {
|
|||||||
is_system_group: true,
|
is_system_group: true,
|
||||||
direct_subgroup_ids: new Set([]),
|
direct_subgroup_ids: new Set([]),
|
||||||
};
|
};
|
||||||
|
const students = {
|
||||||
|
name: "Students",
|
||||||
|
id: 5,
|
||||||
|
members: new Set([member_user_id]),
|
||||||
|
is_system_group: false,
|
||||||
|
direct_subgroup_ids: new Set([]),
|
||||||
|
};
|
||||||
|
|
||||||
user_groups.initialize({realm_user_groups: [admins, moderators, all, nobody]});
|
user_groups.initialize({realm_user_groups: [admins, moderators, all, nobody, students]});
|
||||||
|
|
||||||
const sub = {
|
const sub = {
|
||||||
name: "Denmark",
|
name: "Denmark",
|
||||||
@@ -1187,10 +1194,17 @@ test("can_unsubscribe_others", ({override}) => {
|
|||||||
people.initialize_current_user(member_user_id);
|
people.initialize_current_user(member_user_id);
|
||||||
assert.equal(stream_data.can_unsubscribe_others(sub), true);
|
assert.equal(stream_data.can_unsubscribe_others(sub), true);
|
||||||
|
|
||||||
// With the nobody system group, admins cannot unsubscribe others.
|
// With the setting set to user defined group not including admin,
|
||||||
sub.can_remove_subscribers_group = nobody.id;
|
// admin can still unsubscribe others.
|
||||||
|
sub.can_remove_subscribers_group = students.id;
|
||||||
override(current_user, "is_admin", true);
|
override(current_user, "is_admin", true);
|
||||||
|
people.initialize_current_user(admin_user_id);
|
||||||
|
assert.equal(stream_data.can_unsubscribe_others(sub), true);
|
||||||
|
override(current_user, "is_admin", false);
|
||||||
|
people.initialize_current_user(moderator_user_id);
|
||||||
assert.equal(stream_data.can_unsubscribe_others(sub), false);
|
assert.equal(stream_data.can_unsubscribe_others(sub), false);
|
||||||
|
people.initialize_current_user(member_user_id);
|
||||||
|
assert.equal(stream_data.can_unsubscribe_others(sub), true);
|
||||||
|
|
||||||
// This isn't a real state, but we want coverage on !can_view_subscribers.
|
// This isn't a real state, but we want coverage on !can_view_subscribers.
|
||||||
sub.can_remove_subscribers_group = all.id;
|
sub.can_remove_subscribers_group = all.id;
|
||||||
|
|||||||
@@ -675,6 +675,9 @@ def can_remove_subscribers_from_stream(
|
|||||||
if not check_basic_stream_access(user_profile, stream, sub, allow_realm_admin=True):
|
if not check_basic_stream_access(user_profile, stream, sub, allow_realm_admin=True):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if user_profile.is_realm_admin:
|
||||||
|
return True
|
||||||
|
|
||||||
group_allowed_to_remove_subscribers = stream.can_remove_subscribers_group
|
group_allowed_to_remove_subscribers = stream.can_remove_subscribers_group
|
||||||
assert group_allowed_to_remove_subscribers is not None
|
assert group_allowed_to_remove_subscribers is not None
|
||||||
return user_has_permission_for_group_setting(
|
return user_has_permission_for_group_setting(
|
||||||
|
|||||||
@@ -19992,6 +19992,8 @@ paths:
|
|||||||
The set of users who have permission to unsubscribe others from this
|
The set of users who have permission to unsubscribe others from this
|
||||||
channel expressed as an [update to a group-setting value][update-group-setting].
|
channel expressed as an [update to a group-setting value][update-group-setting].
|
||||||
|
|
||||||
|
Administrators can always unsubscribe others from a channel.
|
||||||
|
|
||||||
Note that a user who is a member of the specified user group must
|
Note that a user who is a member of the specified user group must
|
||||||
also [have access](/help/channel-permissions) to the channel in
|
also [have access](/help/channel-permissions) to the channel in
|
||||||
order to unsubscribe others.
|
order to unsubscribe others.
|
||||||
@@ -24570,6 +24572,8 @@ components:
|
|||||||
A [group-setting value][setting-values] defining the set of users
|
A [group-setting value][setting-values] defining the set of users
|
||||||
who have permission to remove subscribers from this channel.
|
who have permission to remove subscribers from this channel.
|
||||||
|
|
||||||
|
Administrators can always unsubscribe others from a channel.
|
||||||
|
|
||||||
Note that a user who is a member of the specified user group must
|
Note that a user who is a member of the specified user group must
|
||||||
also [have access](/help/channel-permissions) to the channel in
|
also [have access](/help/channel-permissions) to the channel in
|
||||||
order to unsubscribe others.
|
order to unsubscribe others.
|
||||||
|
|||||||
@@ -2714,7 +2714,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
those you aren't on.
|
those you aren't on.
|
||||||
"""
|
"""
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=17,
|
query_count=15,
|
||||||
target_users=[self.example_user("cordelia")],
|
target_users=[self.example_user("cordelia")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=True,
|
is_subbed=True,
|
||||||
@@ -2741,7 +2741,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
for name in ["cordelia", "prospero", "iago", "hamlet", "outgoing_webhook_bot"]
|
for name in ["cordelia", "prospero", "iago", "hamlet", "outgoing_webhook_bot"]
|
||||||
]
|
]
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=24,
|
query_count=22,
|
||||||
cache_count=8,
|
cache_count=8,
|
||||||
target_users=target_users,
|
target_users=target_users,
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
@@ -2759,7 +2759,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
are on.
|
are on.
|
||||||
"""
|
"""
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=18,
|
query_count=17,
|
||||||
target_users=[self.example_user("cordelia")],
|
target_users=[self.example_user("cordelia")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=True,
|
is_subbed=True,
|
||||||
@@ -2776,7 +2776,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
streams you aren't on.
|
streams you aren't on.
|
||||||
"""
|
"""
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=18,
|
query_count=17,
|
||||||
target_users=[self.example_user("cordelia")],
|
target_users=[self.example_user("cordelia")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=False,
|
is_subbed=False,
|
||||||
@@ -2802,7 +2802,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
|
|
||||||
def test_admin_remove_others_from_stream_legacy_emails(self) -> None:
|
def test_admin_remove_others_from_stream_legacy_emails(self) -> None:
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=17,
|
query_count=15,
|
||||||
target_users=[self.example_user("cordelia")],
|
target_users=[self.example_user("cordelia")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=True,
|
is_subbed=True,
|
||||||
@@ -2816,7 +2816,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
|
|
||||||
def test_admin_remove_multiple_users_from_stream_legacy_emails(self) -> None:
|
def test_admin_remove_multiple_users_from_stream_legacy_emails(self) -> None:
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=19,
|
query_count=17,
|
||||||
target_users=[self.example_user("cordelia"), self.example_user("prospero")],
|
target_users=[self.example_user("cordelia"), self.example_user("prospero")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=True,
|
is_subbed=True,
|
||||||
@@ -2830,7 +2830,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
|
|
||||||
def test_remove_unsubbed_user_along_with_subbed(self) -> None:
|
def test_remove_unsubbed_user_along_with_subbed(self) -> None:
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=16,
|
query_count=14,
|
||||||
target_users=[self.example_user("cordelia"), self.example_user("iago")],
|
target_users=[self.example_user("cordelia"), self.example_user("iago")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=True,
|
is_subbed=True,
|
||||||
@@ -2847,7 +2847,7 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
fails gracefully.
|
fails gracefully.
|
||||||
"""
|
"""
|
||||||
result = self.attempt_unsubscribe_of_principal(
|
result = self.attempt_unsubscribe_of_principal(
|
||||||
query_count=9,
|
query_count=7,
|
||||||
target_users=[self.example_user("cordelia")],
|
target_users=[self.example_user("cordelia")],
|
||||||
is_realm_admin=True,
|
is_realm_admin=True,
|
||||||
is_subbed=False,
|
is_subbed=False,
|
||||||
@@ -2929,8 +2929,10 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
self.assert_length(json["not_removed"], 0)
|
self.assert_length(json["not_removed"], 0)
|
||||||
|
|
||||||
check_unsubscribing_user(self.example_user("hamlet"), leadership_group, expect_fail=True)
|
check_unsubscribing_user(self.example_user("hamlet"), leadership_group, expect_fail=True)
|
||||||
check_unsubscribing_user(self.example_user("desdemona"), leadership_group, expect_fail=True)
|
|
||||||
check_unsubscribing_user(self.example_user("iago"), leadership_group)
|
check_unsubscribing_user(self.example_user("iago"), leadership_group)
|
||||||
|
# Owners can always unsubscribe others even when they are not a member
|
||||||
|
# allowed group.
|
||||||
|
check_unsubscribing_user(self.example_user("desdemona"), leadership_group)
|
||||||
|
|
||||||
check_unsubscribing_user(self.example_user("othello"), managers_group, expect_fail=True)
|
check_unsubscribing_user(self.example_user("othello"), managers_group, expect_fail=True)
|
||||||
check_unsubscribing_user(self.example_user("shiva"), managers_group)
|
check_unsubscribing_user(self.example_user("shiva"), managers_group)
|
||||||
@@ -2942,7 +2944,9 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
# are not subscribed to even if they are member of the allowed group.
|
# are not subscribed to even if they are member of the allowed group.
|
||||||
check_unsubscribing_user(self.example_user("shiva"), leadership_group, expect_fail=True)
|
check_unsubscribing_user(self.example_user("shiva"), leadership_group, expect_fail=True)
|
||||||
check_unsubscribing_user(self.example_user("iago"), leadership_group)
|
check_unsubscribing_user(self.example_user("iago"), leadership_group)
|
||||||
|
# Owners can always unsubscribe others even when they are not a member
|
||||||
|
# allowed group.
|
||||||
|
check_unsubscribing_user(self.example_user("desdemona"), leadership_group)
|
||||||
self.subscribe(self.example_user("shiva"), stream.name)
|
self.subscribe(self.example_user("shiva"), stream.name)
|
||||||
check_unsubscribing_user(self.example_user("shiva"), leadership_group)
|
check_unsubscribing_user(self.example_user("shiva"), leadership_group)
|
||||||
|
|
||||||
@@ -2952,11 +2956,19 @@ class StreamAdminTest(ZulipTestCase):
|
|||||||
[leadership_group],
|
[leadership_group],
|
||||||
)
|
)
|
||||||
check_unsubscribing_user(self.example_user("othello"), setting_group, expect_fail=True)
|
check_unsubscribing_user(self.example_user("othello"), setting_group, expect_fail=True)
|
||||||
check_unsubscribing_user(self.example_user("desdemona"), setting_group, expect_fail=True)
|
|
||||||
check_unsubscribing_user(self.example_user("hamlet"), setting_group)
|
check_unsubscribing_user(self.example_user("hamlet"), setting_group)
|
||||||
check_unsubscribing_user(self.example_user("iago"), setting_group)
|
check_unsubscribing_user(self.example_user("iago"), setting_group)
|
||||||
check_unsubscribing_user(self.example_user("shiva"), setting_group)
|
check_unsubscribing_user(self.example_user("shiva"), setting_group)
|
||||||
|
|
||||||
|
# Admins can unsubscribe others even when they are not a member of the
|
||||||
|
# allowed group.
|
||||||
|
setting_group = self.create_or_update_anonymous_group_for_setting(
|
||||||
|
[hamlet],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
check_unsubscribing_user(self.example_user("desdemona"), setting_group)
|
||||||
|
check_unsubscribing_user(self.example_user("iago"), setting_group)
|
||||||
|
|
||||||
def test_remove_invalid_user(self) -> None:
|
def test_remove_invalid_user(self) -> None:
|
||||||
"""
|
"""
|
||||||
Trying to unsubscribe an invalid user from a stream fails gracefully.
|
Trying to unsubscribe an invalid user from a stream fails gracefully.
|
||||||
|
|||||||
Reference in New Issue
Block a user