mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	stream settings: Allow realm admins to update any stream name & description.
This will allow realm admins to update the names and descriptions of private streams even if they are not subscribed, which fixes the buggy behavior that previously nobody could(!).
This commit is contained in:
		@@ -101,14 +101,20 @@ strength allowed is controlled by two settings in
 | 
				
			|||||||
  without joining the stream.
 | 
					  without joining the stream.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* A private ("invite-only") stream is hidden from users who are not
 | 
					* A private ("invite-only") stream is hidden from users who are not
 | 
				
			||||||
  subscribed to the stream.  Users who are not members of a private
 | 
					  subscribed to the stream.
 | 
				
			||||||
  stream cannot read messages on the stream, send messages to the
 | 
					  * Users who are not members of a private stream cannot read messages
 | 
				
			||||||
  stream, or join the stream, even if they are a Zulip realm
 | 
					    on the stream, send messages to the stream, or join the stream,
 | 
				
			||||||
  administrator.  Users can join private streams only when they are
 | 
					    even if they are a Zulip organization administrator.
 | 
				
			||||||
  invited.  However, any member of a private stream can invite other
 | 
					  * Any member of a private stream can add other users to the stream.
 | 
				
			||||||
  users to the stream.  When a new user joins a private stream, they
 | 
					    This is the only way that one can join a private stream (even
 | 
				
			||||||
  can see future messages sent to the stream, but they do not receive
 | 
					    organization administrators cannot join a private stream without
 | 
				
			||||||
  access to the stream's message history.
 | 
					    being added by an existing member).
 | 
				
			||||||
 | 
					  * When a new user joins a private stream, they can see future
 | 
				
			||||||
 | 
					    messages sent to the stream, but they do not receive access to the
 | 
				
			||||||
 | 
					    stream's message history.
 | 
				
			||||||
 | 
					  * Organization administrators can do some basic management of
 | 
				
			||||||
 | 
					    private streams that they are not subscribed to: Changing the
 | 
				
			||||||
 | 
					    stream name and setting its description.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Zulip supports editing the content and topics of messages that have
 | 
					* Zulip supports editing the content and topics of messages that have
 | 
				
			||||||
  already been sent. As a general philosophy, our policies provide
 | 
					  already been sent. As a general philosophy, our policies provide
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -194,10 +194,9 @@ exports.render_stream_description = function (sub) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
exports.update_calculated_fields = function (sub) {
 | 
					exports.update_calculated_fields = function (sub) {
 | 
				
			||||||
    sub.is_admin = page_params.is_admin;
 | 
					    sub.is_admin = page_params.is_admin;
 | 
				
			||||||
    // Admin can change stream name/description either stream is public or
 | 
					    // Admin can change any stream's name & description either stream is public or
 | 
				
			||||||
    // stream is private and admin is subscribed to private stream.
 | 
					    // private, subscribed or unsubscribed.
 | 
				
			||||||
    sub.can_change_name_description = page_params.is_admin &&
 | 
					    sub.can_change_name_description = page_params.is_admin;
 | 
				
			||||||
                                     (!sub.invite_only || (sub.invite_only && sub.subscribed));
 | 
					 | 
				
			||||||
    // If stream is public then any user can subscribe. If stream is private then only
 | 
					    // If stream is public then any user can subscribe. If stream is private then only
 | 
				
			||||||
    // subscribed users can unsubscribe.
 | 
					    // subscribed users can unsubscribe.
 | 
				
			||||||
    sub.should_display_subscription_button = !sub.invite_only || sub.subscribed;
 | 
					    sub.should_display_subscription_button = !sub.invite_only || sub.subscribed;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,11 +10,11 @@ from zerver.models import UserProfile, Stream, Subscription, \
 | 
				
			|||||||
    Realm, Recipient, bulk_get_recipients, get_stream_recipient, get_stream, \
 | 
					    Realm, Recipient, bulk_get_recipients, get_stream_recipient, get_stream, \
 | 
				
			||||||
    bulk_get_streams, get_realm_stream, DefaultStreamGroup
 | 
					    bulk_get_streams, get_realm_stream, DefaultStreamGroup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def access_stream_for_delete(user_profile: UserProfile, stream_id: int) -> Stream:
 | 
					def access_stream_for_delete_or_update(user_profile: UserProfile, stream_id: int) -> Stream:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # We should only ever use this for realm admins, who are allowed
 | 
					    # We should only ever use this for realm admins, who are allowed
 | 
				
			||||||
    # to delete all streams on their realm, even private streams to
 | 
					    # to delete or update all streams on their realm, even private streams
 | 
				
			||||||
    # which they are not subscribed.  We do an assert here, because
 | 
					    # to which they are not subscribed.  We do an assert here, because
 | 
				
			||||||
    # all callers should have the require_realm_admin decorator.
 | 
					    # all callers should have the require_realm_admin decorator.
 | 
				
			||||||
    assert(user_profile.is_realm_admin)
 | 
					    assert(user_profile.is_realm_admin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -401,6 +401,29 @@ class StreamAdminTest(ZulipTestCase):
 | 
				
			|||||||
                                   {'new_name': ujson.dumps('stream_name2')})
 | 
					                                   {'new_name': ujson.dumps('stream_name2')})
 | 
				
			||||||
        self.assert_json_error(result, 'Must be an organization administrator')
 | 
					        self.assert_json_error(result, 'Must be an organization administrator')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_realm_admin_can_update_unsub_private_stream(self) -> None:
 | 
				
			||||||
 | 
					        iago = self.example_user('iago')
 | 
				
			||||||
 | 
					        self.login(iago.email)
 | 
				
			||||||
 | 
					        result = self.common_subscribe_to_streams(iago.email, ["private_stream"],
 | 
				
			||||||
 | 
					                                                  dict(principals=ujson.dumps([self.example_email("hamlet")])),
 | 
				
			||||||
 | 
					                                                  invite_only=True)
 | 
				
			||||||
 | 
					        self.assert_json_success(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stream_id = get_stream('private_stream', iago.realm).id
 | 
				
			||||||
 | 
					        result = self.client_patch('/json/streams/%d' % (stream_id,),
 | 
				
			||||||
 | 
					                                   {'new_name': ujson.dumps('new_private_stream')})
 | 
				
			||||||
 | 
					        self.assert_json_success(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = self.client_patch('/json/streams/%d' % (stream_id,),
 | 
				
			||||||
 | 
					                                   {'new_description': ujson.dumps('new description')})
 | 
				
			||||||
 | 
					        self.assert_json_success(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # But can not change stream type.
 | 
				
			||||||
 | 
					        result = self.client_patch('/json/streams/%d' % (stream_id,),
 | 
				
			||||||
 | 
					                                   {'stream_name': ujson.dumps('private_stream'),
 | 
				
			||||||
 | 
					                                    'is_private': ujson.dumps(True)})
 | 
				
			||||||
 | 
					        self.assert_json_error(result, "Invalid stream id")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_change_stream_description(self) -> None:
 | 
					    def test_change_stream_description(self) -> None:
 | 
				
			||||||
        user_profile = self.example_user('hamlet')
 | 
					        user_profile = self.example_user('hamlet')
 | 
				
			||||||
        email = user_profile.email
 | 
					        email = user_profile.email
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@ from zerver.lib.actions import bulk_remove_subscriptions, \
 | 
				
			|||||||
from zerver.lib.response import json_success, json_error, json_response
 | 
					from zerver.lib.response import json_success, json_error, json_response
 | 
				
			||||||
from zerver.lib.streams import access_stream_by_id, access_stream_by_name, \
 | 
					from zerver.lib.streams import access_stream_by_id, access_stream_by_name, \
 | 
				
			||||||
    check_stream_name, check_stream_name_available, filter_stream_authorization, \
 | 
					    check_stream_name, check_stream_name_available, filter_stream_authorization, \
 | 
				
			||||||
    list_to_streams, access_stream_for_delete, access_default_stream_group_by_id
 | 
					    list_to_streams, access_stream_for_delete_or_update, access_default_stream_group_by_id
 | 
				
			||||||
from zerver.lib.validator import check_string, check_int, check_list, check_dict, \
 | 
					from zerver.lib.validator import check_string, check_int, check_list, check_dict, \
 | 
				
			||||||
    check_bool, check_variable_type
 | 
					    check_bool, check_variable_type
 | 
				
			||||||
from zerver.models import UserProfile, Stream, Realm, Subscription, \
 | 
					from zerver.models import UserProfile, Stream, Realm, Subscription, \
 | 
				
			||||||
@@ -61,7 +61,7 @@ def principal_to_user_profile(agent: UserProfile, principal: Text) -> UserProfil
 | 
				
			|||||||
def deactivate_stream_backend(request: HttpRequest,
 | 
					def deactivate_stream_backend(request: HttpRequest,
 | 
				
			||||||
                              user_profile: UserProfile,
 | 
					                              user_profile: UserProfile,
 | 
				
			||||||
                              stream_id: int) -> HttpResponse:
 | 
					                              stream_id: int) -> HttpResponse:
 | 
				
			||||||
    stream = access_stream_for_delete(user_profile, stream_id)
 | 
					    stream = access_stream_for_delete_or_update(user_profile, stream_id)
 | 
				
			||||||
    do_deactivate_stream(stream)
 | 
					    do_deactivate_stream(stream)
 | 
				
			||||||
    return json_success()
 | 
					    return json_success()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -148,8 +148,9 @@ def update_stream_backend(
 | 
				
			|||||||
        is_private: Optional[bool]=REQ(validator=check_bool, default=None),
 | 
					        is_private: Optional[bool]=REQ(validator=check_bool, default=None),
 | 
				
			||||||
        new_name: Optional[Text]=REQ(validator=check_string, default=None),
 | 
					        new_name: Optional[Text]=REQ(validator=check_string, default=None),
 | 
				
			||||||
) -> HttpResponse:
 | 
					) -> HttpResponse:
 | 
				
			||||||
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
 | 
					    # We allow realm administrators to to update the stream name and
 | 
				
			||||||
 | 
					    # description even for private streams.
 | 
				
			||||||
 | 
					    stream = access_stream_for_delete_or_update(user_profile, stream_id)
 | 
				
			||||||
    if description is not None:
 | 
					    if description is not None:
 | 
				
			||||||
        do_change_stream_description(stream, description)
 | 
					        do_change_stream_description(stream, description)
 | 
				
			||||||
    if new_name is not None:
 | 
					    if new_name is not None:
 | 
				
			||||||
@@ -161,7 +162,11 @@ def update_stream_backend(
 | 
				
			|||||||
            # are only changing the casing of the stream name).
 | 
					            # are only changing the casing of the stream name).
 | 
				
			||||||
            check_stream_name_available(user_profile.realm, new_name)
 | 
					            check_stream_name_available(user_profile.realm, new_name)
 | 
				
			||||||
        do_rename_stream(stream, new_name)
 | 
					        do_rename_stream(stream, new_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # But we require even realm administrators to be actually
 | 
				
			||||||
 | 
					    # subscribed to make a private stream public.
 | 
				
			||||||
    if is_private is not None:
 | 
					    if is_private is not None:
 | 
				
			||||||
 | 
					        (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
 | 
				
			||||||
        do_change_stream_invite_only(stream, is_private)
 | 
					        do_change_stream_invite_only(stream, is_private)
 | 
				
			||||||
    return json_success()
 | 
					    return json_success()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user