示例#1
0
def update_user_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    user_id: int,
    full_name: Optional[str] = REQ(default=None, validator=check_string),
    role: Optional[int] = REQ(default=None,
                              validator=check_int_in(
                                  UserProfile.ROLE_TYPES, )),
    profile_data: Optional[List[Dict[str, Optional[Union[
        int, str, List[int]]]]]] = REQ(
            default=None,
            validator=check_profile_data,
        ),
) -> HttpResponse:
    target = access_user_by_id(user_profile,
                               user_id,
                               allow_deactivated=True,
                               allow_bots=True,
                               for_admin=True)

    if role is not None and target.role != role:
        # Require that the current user has permissions to
        # grant/remove the role in question.  access_user_by_id has
        # already verified we're an administrator; here we enforce
        # that only owners can toggle the is_realm_owner flag.
        if UserProfile.ROLE_REALM_OWNER in [
                role, target.role
        ] and not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()

        if target.role == UserProfile.ROLE_REALM_OWNER and check_last_owner(
                user_profile):
            return json_error(
                _('The owner permission cannot be removed from the only organization owner.'
                  ))
        do_change_user_role(target, role, acting_user=user_profile)

    if (full_name is not None and target.full_name != full_name
            and full_name.strip() != ""):
        # We don't respect `name_changes_disabled` here because the request
        # is on behalf of the administrator.
        check_change_full_name(target, full_name, user_profile)

    if profile_data is not None:
        clean_profile_data = []
        for entry in profile_data:
            assert isinstance(entry["id"], int)
            if entry["value"] is None or not entry["value"]:
                field_id = entry["id"]
                check_remove_custom_profile_field_value(target, field_id)
            else:
                clean_profile_data.append({
                    "id": entry["id"],
                    "value": entry["value"],
                })
        validate_user_custom_profile_data(target.realm.id, clean_profile_data)
        do_update_user_custom_profile_data_if_changed(target,
                                                      clean_profile_data)

    return json_success()
示例#2
0
def update_user_backend(request: HttpRequest, user_profile: UserProfile, user_id: int,
                        full_name: Optional[str]=REQ(default=None, validator=check_string),
                        role: Optional[int]=REQ(default=None, validator=check_int_in(
                            UserProfile.ROLE_TYPES)),
                        profile_data: Optional[List[Dict[str, Union[int, str, List[int]]]]]=
                        REQ(default=None,
                            validator=check_list(check_dict([('id', check_int)])))) -> HttpResponse:
    target = access_user_by_id(user_profile, user_id, allow_deactivated=True, allow_bots=True)

    if role is not None and target.role != role:
        if target.role == UserProfile.ROLE_REALM_OWNER and check_last_owner(user_profile):
            return json_error(_('The owner permission cannot be removed from the only organization owner.'))
        if UserProfile.ROLE_REALM_OWNER in [role, target.role] and not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        do_change_user_role(target, role)

    if (full_name is not None and target.full_name != full_name and
            full_name.strip() != ""):
        # We don't respect `name_changes_disabled` here because the request
        # is on behalf of the administrator.
        check_change_full_name(target, full_name, user_profile)

    if profile_data is not None:
        clean_profile_data = []
        for entry in profile_data:
            if not entry["value"]:
                field_id = entry["id"]
                check_remove_custom_profile_field_value(target, field_id)
            else:
                clean_profile_data.append(entry)
        validate_user_custom_profile_data(target.realm.id, clean_profile_data)
        do_update_user_custom_profile_data_if_changed(target, clean_profile_data)

    return json_success()
示例#3
0
文件: users.py 项目: mrchntia/zulip
def deactivate_user_backend(request: HttpRequest, user_profile: UserProfile,
                            user_id: int) -> HttpResponse:
    target = access_user_by_id(user_profile, user_id, for_admin=True)
    if target.is_realm_owner and not user_profile.is_realm_owner:
        raise OrganizationOwnerRequired()
    if check_last_owner(target):
        raise JsonableError(_("Cannot deactivate the only organization owner"))
    return _deactivate_user_profile_backend(request, user_profile, target)
示例#4
0
def delete_user_backend(request: HttpRequest, user_profile: UserProfile,
                        user_id: int) -> HttpResponse:
    target = acces_user_by_id(user_profile, user_id)
    if target.is_realm_owner and not user_profile.is_realm_owner:
        raise OrganizationOwnerRequired()
    if check_last_owner(target):
        return json_error(_('Cannot delete the only organization owner'))
    return json_success()
示例#5
0
def update_stream_backend(
        request: HttpRequest, user_profile: UserProfile,
        stream_id: int,
        description: Optional[str]=REQ(validator=check_capped_string(
            Stream.MAX_DESCRIPTION_LENGTH), default=None),
        is_private: Optional[bool]=REQ(validator=check_bool, default=None),
        is_announcement_only: Optional[bool]=REQ(validator=check_bool, default=None),
        stream_post_policy: Optional[int]=REQ(validator=check_int_in(
            Stream.STREAM_POST_POLICY_TYPES), default=None),
        history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None),
        new_name: Optional[str]=REQ(validator=check_string, default=None),
        message_retention_days: Optional[Union[int, str]]=REQ(validator=check_string_or_int, default=None),
) -> HttpResponse:
    # 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 message_retention_days is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        user_profile.realm.ensure_not_on_limited_plan()
        message_retention_days_value = parse_message_retention_days(
            message_retention_days, Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)
        do_change_stream_message_retention_days(stream, message_retention_days_value)

    if description is not None:
        if '\n' in description:
            # We don't allow newline characters in stream descriptions.
            description = description.replace("\n", " ")
        do_change_stream_description(stream, description)
    if new_name is not None:
        new_name = new_name.strip()
        if stream.name == new_name:
            return json_error(_("Stream already has that name!"))
        if stream.name.lower() != new_name.lower():
            # Check that the stream name is available (unless we are
            # are only changing the casing of the stream name).
            check_stream_name_available(user_profile.realm, new_name)
        do_rename_stream(stream, new_name, user_profile)
    if is_announcement_only is not None:
        # is_announcement_only is a legacy way to specify
        # stream_post_policy.  We can probably just delete this code,
        # since we're not aware of clients that used it, but we're
        # keeping it for backwards-compatibility for now.
        stream_post_policy = Stream.STREAM_POST_POLICY_EVERYONE
        if is_announcement_only:
            stream_post_policy = Stream.STREAM_POST_POLICY_ADMINS
    if stream_post_policy is not None:
        do_change_stream_post_policy(stream, stream_post_policy)

    # But we require even realm administrators to be actually
    # subscribed to make a private stream public.
    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, history_public_to_subscribers)
    return json_success()
示例#6
0
def access_bot_by_id(user_profile: UserProfile, user_id: int) -> UserProfile:
    try:
        target = get_user_profile_by_id_in_realm(user_id, user_profile.realm)
    except UserProfile.DoesNotExist:
        raise JsonableError(_("No such bot"))
    if not target.is_bot:
        raise JsonableError(_("No such bot"))
    if not user_profile.can_admin_user(target):
        raise JsonableError(_("Insufficient permission"))

    if target.can_create_users and not user_profile.is_realm_owner:
        # Organizations owners are required to administer a bot with
        # the can_create_users permission. User creation via the API
        # is a permission not available even to organization owners by
        # default, because it can be abused to send spam. Requiring an
        # owner is intended to ensure organizational responsibility
        # for use of this permission.
        raise OrganizationOwnerRequired()

    return target
示例#7
0
def list_to_streams(
    streams_raw: Collection[StreamDict],
    user_profile: UserProfile,
    autocreate: bool = False,
    admin_access_required: bool = False,
) -> Tuple[List[Stream], List[Stream]]:
    """Converts list of dicts to a list of Streams, validating input in the process

    For each stream name, we validate it to ensure it meets our
    requirements for a proper stream name using check_stream_name.

    This function in autocreate mode should be atomic: either an exception will be raised
    during a precheck, or all the streams specified will have been created if applicable.

    @param streams_raw The list of stream dictionaries to process;
      names should already be stripped of whitespace by the caller.
    @param user_profile The user for whom we are retrieving the streams
    @param autocreate Whether we should create streams if they don't already exist
    """
    # Validate all streams, getting extant ones, then get-or-creating the rest.

    stream_set = {stream_dict["name"] for stream_dict in streams_raw}

    for stream_name in stream_set:
        # Stream names should already have been stripped by the
        # caller, but it makes sense to verify anyway.
        assert stream_name == stream_name.strip()
        check_stream_name(stream_name)

    existing_streams: List[Stream] = []
    missing_stream_dicts: List[StreamDict] = []
    existing_stream_map = bulk_get_streams(user_profile.realm, stream_set)

    if admin_access_required:
        existing_recipient_ids = [
            stream.recipient_id for stream in existing_stream_map.values()
        ]
        subs = Subscription.objects.filter(
            user_profile=user_profile,
            recipient_id__in=existing_recipient_ids,
            active=True)
        sub_map = {sub.recipient_id: sub for sub in subs}
        for stream in existing_stream_map.values():
            sub = sub_map.get(stream.recipient_id, None)
            check_stream_access_for_delete_or_update(user_profile, stream, sub)

    message_retention_days_not_none = False
    web_public_stream_requested = False
    for stream_dict in streams_raw:
        stream_name = stream_dict["name"]
        stream = existing_stream_map.get(stream_name.lower())
        if stream is None:
            if stream_dict.get("message_retention_days", None) is not None:
                message_retention_days_not_none = True
            missing_stream_dicts.append(stream_dict)

            if autocreate and stream_dict["is_web_public"]:
                web_public_stream_requested = True
        else:
            existing_streams.append(stream)

    if len(missing_stream_dicts) == 0:
        # This is the happy path for callers who expected all of these
        # streams to exist already.
        created_streams: List[Stream] = []
    else:
        # autocreate=True path starts here
        for stream_dict in missing_stream_dicts:
            invite_only = stream_dict.get("invite_only", False)
            if invite_only and not user_profile.can_create_private_streams():
                raise JsonableError(_("Insufficient permission"))
            if not invite_only and not user_profile.can_create_public_streams(
            ):
                raise JsonableError(_("Insufficient permission"))

        if not autocreate:
            raise JsonableError(
                _("Stream(s) ({}) do not exist").format(
                    ", ".join(stream_dict["name"]
                              for stream_dict in missing_stream_dicts), ))

        if web_public_stream_requested:
            if not user_profile.realm.web_public_streams_enabled():
                raise JsonableError(_("Web public streams are not enabled."))
            if not user_profile.can_create_web_public_streams():
                # We set create_web_public_stream_policy to allow only organization owners
                # to create web-public streams, because of their sensitive nature.
                raise JsonableError(_("Insufficient permission"))

        if message_retention_days_not_none:
            if not user_profile.is_realm_owner:
                raise OrganizationOwnerRequired()

            user_profile.realm.ensure_not_on_limited_plan()

        # We already filtered out existing streams, so dup_streams
        # will normally be an empty list below, but we protect against somebody
        # else racing to create the same stream.  (This is not an entirely
        # paranoid approach, since often on Zulip two people will discuss
        # creating a new stream, and both people eagerly do it.)
        created_streams, dup_streams = create_streams_if_needed(
            realm=user_profile.realm,
            stream_dicts=missing_stream_dicts,
            acting_user=user_profile)
        existing_streams += dup_streams

    return existing_streams, created_streams
示例#8
0
def update_realm(
    request: HttpRequest,
    user_profile: UserProfile,
    name: Optional[str] = REQ(str_validator=check_capped_string(
        Realm.MAX_REALM_NAME_LENGTH),
                              default=None),
    description: Optional[str] = REQ(str_validator=check_capped_string(
        Realm.MAX_REALM_DESCRIPTION_LENGTH),
                                     default=None),
    emails_restricted_to_domains: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    disallow_disposable_email_addresses: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    invite_required: Optional[bool] = REQ(json_validator=check_bool,
                                          default=None),
    invite_to_realm_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.INVITE_TO_REALM_POLICY_TYPES),
                                                default=None),
    name_changes_disabled: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    email_changes_disabled: Optional[bool] = REQ(json_validator=check_bool,
                                                 default=None),
    avatar_changes_disabled: Optional[bool] = REQ(json_validator=check_bool,
                                                  default=None),
    inline_image_preview: Optional[bool] = REQ(json_validator=check_bool,
                                               default=None),
    inline_url_embed_preview: Optional[bool] = REQ(json_validator=check_bool,
                                                   default=None),
    add_custom_emoji_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                 default=None),
    delete_own_message_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_MESSAGE_POLICY_TYPES),
                                                   default=None),
    message_content_delete_limit_seconds_raw: Optional[Union[int, str]] = REQ(
        "message_content_delete_limit_seconds",
        json_validator=check_string_or_int,
        default=None),
    allow_message_editing: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    edit_topic_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_MESSAGE_POLICY_TYPES),
                                           default=None),
    mandatory_topics: Optional[bool] = REQ(json_validator=check_bool,
                                           default=None),
    message_content_edit_limit_seconds: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    allow_edit_history: Optional[bool] = REQ(json_validator=check_bool,
                                             default=None),
    default_language: Optional[str] = REQ(default=None),
    waiting_period_threshold: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    authentication_methods: Optional[Dict[str, Any]] = REQ(
        json_validator=check_dict([]), default=None),
    notifications_stream_id: Optional[int] = REQ(json_validator=check_int,
                                                 default=None),
    signup_notifications_stream_id: Optional[int] = REQ(
        json_validator=check_int, default=None),
    message_retention_days_raw: Optional[Union[int, str]] = REQ(
        "message_retention_days",
        json_validator=check_string_or_int,
        default=None),
    send_welcome_emails: Optional[bool] = REQ(json_validator=check_bool,
                                              default=None),
    digest_emails_enabled: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    message_content_allowed_in_email_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    bot_creation_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.BOT_CREATION_POLICY_TYPES),
                                             default=None),
    create_public_stream_policy: Optional[int] = REQ(
        json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None),
    create_private_stream_policy: Optional[int] = REQ(
        json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None),
    create_web_public_stream_policy: Optional[int] = REQ(
        json_validator=check_int_in(
            Realm.CREATE_WEB_PUBLIC_STREAM_POLICY_TYPES),
        default=None),
    invite_to_stream_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                 default=None),
    move_messages_between_streams_policy: Optional[int] = REQ(
        json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None),
    user_group_edit_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                default=None),
    private_message_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.PRIVATE_MESSAGE_POLICY_TYPES),
                                                default=None),
    wildcard_mention_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.WILDCARD_MENTION_POLICY_TYPES),
                                                 default=None),
    email_address_visibility: Optional[int] = REQ(json_validator=check_int_in(
        Realm.EMAIL_ADDRESS_VISIBILITY_TYPES),
                                                  default=None),
    video_chat_provider: Optional[int] = REQ(json_validator=check_int,
                                             default=None),
    giphy_rating: Optional[int] = REQ(json_validator=check_int, default=None),
    default_code_block_language: Optional[str] = REQ(default=None),
    digest_weekday: Optional[int] = REQ(json_validator=check_int_in(
        Realm.DIGEST_WEEKDAY_VALUES),
                                        default=None),
    string_id: Optional[str] = REQ(
        str_validator=check_capped_string(Realm.MAX_REALM_SUBDOMAIN_LENGTH),
        default=None,
    ),
    enable_spectator_access: Optional[bool] = REQ(json_validator=check_bool,
                                                  default=None),
) -> HttpResponse:
    realm = user_profile.realm

    # Additional validation/error checking beyond types go here, so
    # the entire request can succeed or fail atomically.
    if default_language is not None and default_language not in get_available_language_codes(
    ):
        raise JsonableError(
            _("Invalid language '{}'").format(default_language))
    if authentication_methods is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        if True not in list(authentication_methods.values()):
            raise JsonableError(
                _("At least one authentication method must be enabled."))
    if video_chat_provider is not None and video_chat_provider not in {
            p["id"]
            for p in Realm.VIDEO_CHAT_PROVIDERS.values()
    }:
        raise JsonableError(
            _("Invalid video_chat_provider {}").format(video_chat_provider))
    if giphy_rating is not None and giphy_rating not in {
            p["id"]
            for p in Realm.GIPHY_RATING_OPTIONS.values()
    }:
        raise JsonableError(_("Invalid giphy_rating {}").format(giphy_rating))

    message_retention_days: Optional[int] = None
    if message_retention_days_raw is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        realm.ensure_not_on_limited_plan()
        message_retention_days = parse_message_retention_days(
            message_retention_days_raw,
            Realm.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)

    if invite_to_realm_policy is not None and not user_profile.is_realm_owner:
        raise OrganizationOwnerRequired()

    data: Dict[str, Any] = {}

    message_content_delete_limit_seconds: Optional[int] = None
    if message_content_delete_limit_seconds_raw is not None:
        message_content_delete_limit_seconds = parse_message_content_delete_limit(
            message_content_delete_limit_seconds_raw,
            Realm.MESSAGE_CONTENT_DELETE_LIMIT_SPECIAL_VALUES_MAP,
        )
        do_set_realm_property(
            realm,
            "message_content_delete_limit_seconds",
            message_content_delete_limit_seconds,
            acting_user=user_profile,
        )
        data[
            "message_content_delete_limit_seconds"] = message_content_delete_limit_seconds

    # The user of `locals()` here is a bit of a code smell, but it's
    # restricted to the elements present in realm.property_types.
    #
    # TODO: It should be possible to deduplicate this function up
    # further by some more advanced usage of the
    # `REQ/has_request_variables` extraction.
    req_vars = {
        k: v
        for k, v in list(locals().items()) if k in realm.property_types
    }

    for k, v in list(req_vars.items()):
        if v is not None and getattr(realm, k) != v:
            do_set_realm_property(realm, k, v, acting_user=user_profile)
            if isinstance(v, str):
                data[k] = "updated"
            else:
                data[k] = v

    # The following realm properties do not fit the pattern above
    # authentication_methods is not supported by the do_set_realm_property
    # framework because of its bitfield.
    if authentication_methods is not None and (
            realm.authentication_methods_dict() != authentication_methods):
        do_set_realm_authentication_methods(realm,
                                            authentication_methods,
                                            acting_user=user_profile)
        data["authentication_methods"] = authentication_methods
    # The message_editing settings are coupled to each other, and thus don't fit
    # into the do_set_realm_property framework.
    if ((allow_message_editing is not None
         and realm.allow_message_editing != allow_message_editing)
            or (message_content_edit_limit_seconds is not None
                and realm.message_content_edit_limit_seconds !=
                message_content_edit_limit_seconds)
            or (edit_topic_policy is not None
                and realm.edit_topic_policy != edit_topic_policy)):
        if allow_message_editing is None:
            allow_message_editing = realm.allow_message_editing
        if message_content_edit_limit_seconds is None:
            message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds
        if edit_topic_policy is None:
            edit_topic_policy = realm.edit_topic_policy
        do_set_realm_message_editing(
            realm,
            allow_message_editing,
            message_content_edit_limit_seconds,
            edit_topic_policy,
            acting_user=user_profile,
        )
        data["allow_message_editing"] = allow_message_editing
        data[
            "message_content_edit_limit_seconds"] = message_content_edit_limit_seconds
        data["edit_topic_policy"] = edit_topic_policy

    # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean,
    # str or integer field, and thus doesn't fit into the do_set_realm_property framework.
    if notifications_stream_id is not None:
        if realm.notifications_stream is None or (realm.notifications_stream.id
                                                  != notifications_stream_id):
            new_notifications_stream = None
            if notifications_stream_id >= 0:
                (new_notifications_stream,
                 sub) = access_stream_by_id(user_profile,
                                            notifications_stream_id)
            do_set_realm_notifications_stream(realm,
                                              new_notifications_stream,
                                              notifications_stream_id,
                                              acting_user=user_profile)
            data["notifications_stream_id"] = notifications_stream_id

    if signup_notifications_stream_id is not None:
        if realm.signup_notifications_stream is None or (
                realm.signup_notifications_stream.id !=
                signup_notifications_stream_id):
            new_signup_notifications_stream = None
            if signup_notifications_stream_id >= 0:
                (new_signup_notifications_stream,
                 sub) = access_stream_by_id(user_profile,
                                            signup_notifications_stream_id)
            do_set_realm_signup_notifications_stream(
                realm,
                new_signup_notifications_stream,
                signup_notifications_stream_id,
                acting_user=user_profile,
            )
            data[
                "signup_notifications_stream_id"] = signup_notifications_stream_id

    if default_code_block_language is not None:
        # Migrate '', used in the API to encode the default/None behavior of this feature.
        if default_code_block_language == "":
            data["default_code_block_language"] = None
        else:
            data["default_code_block_language"] = default_code_block_language

    if string_id is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()

        if realm.demo_organization_scheduled_deletion_date is None:
            raise JsonableError(_("Must be a demo organization."))

        try:
            check_subdomain(string_id)
        except ValidationError as err:
            raise JsonableError(str(err.message))

        do_change_realm_subdomain(realm, string_id, acting_user=user_profile)
        data["realm_uri"] = realm.uri

    return json_success(data)
示例#9
0
 def wrapper(request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object) -> HttpResponse:
     if not user_profile.is_realm_owner:
         raise OrganizationOwnerRequired()
     return func(request, user_profile, *args, **kwargs)
示例#10
0
def update_stream_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int,
    description: Optional[str] = REQ(str_validator=check_capped_string(
        Stream.MAX_DESCRIPTION_LENGTH),
                                     default=None),
    is_private: Optional[bool] = REQ(json_validator=check_bool, default=None),
    is_announcement_only: Optional[bool] = REQ(json_validator=check_bool,
                                               default=None),
    stream_post_policy: Optional[int] = REQ(json_validator=check_int_in(
        Stream.STREAM_POST_POLICY_TYPES),
                                            default=None),
    history_public_to_subscribers: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    is_web_public: Optional[bool] = REQ(json_validator=check_bool,
                                        default=None),
    new_name: Optional[str] = REQ(default=None),
    message_retention_days: Optional[Union[int, str]] = REQ(
        json_validator=check_string_or_int, default=None),
) -> HttpResponse:
    # We allow realm administrators to to update the stream name and
    # description even for private streams.
    (stream, sub) = access_stream_for_delete_or_update(user_profile, stream_id)

    if message_retention_days is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        user_profile.realm.ensure_not_on_limited_plan()
        new_message_retention_days_value = parse_message_retention_days(
            message_retention_days,
            Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)
        do_change_stream_message_retention_days(
            stream, user_profile, new_message_retention_days_value)

    if description is not None:
        if "\n" in description:
            # We don't allow newline characters in stream descriptions.
            description = description.replace("\n", " ")
        do_change_stream_description(stream,
                                     description,
                                     acting_user=user_profile)
    if new_name is not None:
        new_name = new_name.strip()
        if stream.name == new_name:
            raise JsonableError(_("Stream already has that name!"))
        if stream.name.lower() != new_name.lower():
            # Check that the stream name is available (unless we are
            # are only changing the casing of the stream name).
            check_stream_name_available(user_profile.realm, new_name)
        do_rename_stream(stream, new_name, user_profile)
    if is_announcement_only is not None:
        # is_announcement_only is a legacy way to specify
        # stream_post_policy.  We can probably just delete this code,
        # since we're not aware of clients that used it, but we're
        # keeping it for backwards-compatibility for now.
        stream_post_policy = Stream.STREAM_POST_POLICY_EVERYONE
        if is_announcement_only:
            stream_post_policy = Stream.STREAM_POST_POLICY_ADMINS
    if stream_post_policy is not None:
        do_change_stream_post_policy(stream,
                                     stream_post_policy,
                                     acting_user=user_profile)

    # But we require even realm administrators to be actually
    # subscribed to make a private stream public.
    if is_private is not None:
        default_stream_ids = {
            s.id
            for s in get_default_streams_for_realm(stream.realm_id)
        }
        (stream, sub) = access_stream_by_id(user_profile, stream_id)
        if is_private and stream.id in default_stream_ids:
            raise JsonableError(_("Default streams cannot be made private."))

    if is_web_public:
        # Enforce restrictions on creating web-public streams.
        if not user_profile.realm.web_public_streams_enabled():
            raise JsonableError(_("Web-public streams are not enabled."))
        if not user_profile.can_create_web_public_streams():
            raise JsonableError(_("Insufficient permission"))
        # Forbid parameter combinations that are inconsistent
        if is_private or history_public_to_subscribers is False:
            raise JsonableError(_("Invalid parameters"))

    if is_private is not None or is_web_public is not None:
        do_change_stream_permission(
            stream,
            invite_only=is_private,
            history_public_to_subscribers=history_public_to_subscribers,
            is_web_public=is_web_public,
            acting_user=user_profile,
        )
    return json_success(request)
示例#11
0
def check_if_owner_required(invited_as: int, user_profile: UserProfile) -> None:
    if invited_as == PreregistrationUser.INVITE_AS['REALM_OWNER'] and not user_profile.is_realm_owner:
        raise OrganizationOwnerRequired()
示例#12
0
文件: realm.py 项目: yuhuahu310/zulip
def update_realm(
    request: HttpRequest,
    user_profile: UserProfile,
    name: Optional[str] = REQ(validator=check_string, default=None),
    description: Optional[str] = REQ(validator=check_string, default=None),
    emails_restricted_to_domains: Optional[bool] = REQ(validator=check_bool,
                                                       default=None),
    disallow_disposable_email_addresses: Optional[bool] = REQ(
        validator=check_bool, default=None),
    invite_required: Optional[bool] = REQ(validator=check_bool, default=None),
    invite_by_admins_only: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    name_changes_disabled: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    email_changes_disabled: Optional[bool] = REQ(validator=check_bool,
                                                 default=None),
    avatar_changes_disabled: Optional[bool] = REQ(validator=check_bool,
                                                  default=None),
    inline_image_preview: Optional[bool] = REQ(validator=check_bool,
                                               default=None),
    inline_url_embed_preview: Optional[bool] = REQ(validator=check_bool,
                                                   default=None),
    add_emoji_by_admins_only: Optional[bool] = REQ(validator=check_bool,
                                                   default=None),
    allow_message_deleting: Optional[bool] = REQ(validator=check_bool,
                                                 default=None),
    message_content_delete_limit_seconds: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    allow_message_editing: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    allow_community_topic_editing: Optional[bool] = REQ(validator=check_bool,
                                                        default=None),
    mandatory_topics: Optional[bool] = REQ(validator=check_bool, default=None),
    message_content_edit_limit_seconds: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    allow_edit_history: Optional[bool] = REQ(validator=check_bool,
                                             default=None),
    default_language: Optional[str] = REQ(validator=check_string,
                                          default=None),
    waiting_period_threshold: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    authentication_methods: Optional[Dict[str,
                                          Any]] = REQ(validator=check_dict([]),
                                                      default=None),
    notifications_stream_id: Optional[int] = REQ(validator=check_int,
                                                 default=None),
    signup_notifications_stream_id: Optional[int] = REQ(validator=check_int,
                                                        default=None),
    message_retention_days_raw: Optional[Union[int, str]] = REQ(
        "message_retention_days", validator=check_string_or_int, default=None),
    send_welcome_emails: Optional[bool] = REQ(validator=check_bool,
                                              default=None),
    digest_emails_enabled: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    message_content_allowed_in_email_notifications: Optional[bool] = REQ(
        validator=check_bool, default=None),
    bot_creation_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.BOT_CREATION_POLICY_TYPES),
                                             default=None),
    create_stream_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                              default=None),
    invite_to_stream_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                 default=None),
    user_group_edit_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.USER_GROUP_EDIT_POLICY_TYPES),
                                                default=None),
    private_message_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.PRIVATE_MESSAGE_POLICY_TYPES),
                                                default=None),
    email_address_visibility: Optional[int] = REQ(validator=check_int_in(
        Realm.EMAIL_ADDRESS_VISIBILITY_TYPES),
                                                  default=None),
    default_twenty_four_hour_time: Optional[bool] = REQ(validator=check_bool,
                                                        default=None),
    video_chat_provider: Optional[int] = REQ(validator=check_int,
                                             default=None),
    default_code_block_language: Optional[str] = REQ(validator=check_string,
                                                     default=None),
    digest_weekday: Optional[int] = REQ(validator=check_int_in(
        Realm.DIGEST_WEEKDAY_VALUES),
                                        default=None),
) -> HttpResponse:
    realm = user_profile.realm

    # Additional validation/error checking beyond types go here, so
    # the entire request can succeed or fail atomically.
    if default_language is not None and default_language not in get_available_language_codes(
    ):
        raise JsonableError(
            _("Invalid language '{}'").format(default_language))
    if description is not None and len(description) > 1000:
        return json_error(_("Organization description is too long."))
    if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH:
        return json_error(_("Organization name is too long."))
    if authentication_methods is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        if True not in list(authentication_methods.values()):
            return json_error(
                _("At least one authentication method must be enabled."))
    if (video_chat_provider is not None and video_chat_provider
            not in {p['id']
                    for p in Realm.VIDEO_CHAT_PROVIDERS.values()}):
        return json_error(
            _("Invalid video_chat_provider {}").format(video_chat_provider))

    message_retention_days: Optional[int] = None
    if message_retention_days_raw is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        realm.ensure_not_on_limited_plan()
        message_retention_days = parse_message_retention_days(
            message_retention_days_raw,
            Realm.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)

    # The user of `locals()` here is a bit of a code smell, but it's
    # restricted to the elements present in realm.property_types.
    #
    # TODO: It should be possible to deduplicate this function up
    # further by some more advanced usage of the
    # `REQ/has_request_variables` extraction.
    req_vars = {
        k: v
        for k, v in list(locals().items()) if k in realm.property_types
    }
    data: Dict[str, Any] = {}

    for k, v in list(req_vars.items()):
        if v is not None and getattr(realm, k) != v:
            do_set_realm_property(realm, k, v, acting_user=user_profile)
            if isinstance(v, str):
                data[k] = 'updated'
            else:
                data[k] = v

    # The following realm properties do not fit the pattern above
    # authentication_methods is not supported by the do_set_realm_property
    # framework because of its bitfield.
    if authentication_methods is not None and (
            realm.authentication_methods_dict() != authentication_methods):
        do_set_realm_authentication_methods(realm,
                                            authentication_methods,
                                            acting_user=user_profile)
        data['authentication_methods'] = authentication_methods
    # The message_editing settings are coupled to each other, and thus don't fit
    # into the do_set_realm_property framework.
    if ((allow_message_editing is not None
         and realm.allow_message_editing != allow_message_editing)
            or (message_content_edit_limit_seconds is not None
                and realm.message_content_edit_limit_seconds !=
                message_content_edit_limit_seconds)
            or (allow_community_topic_editing is not None
                and realm.allow_community_topic_editing !=
                allow_community_topic_editing)):
        if allow_message_editing is None:
            allow_message_editing = realm.allow_message_editing
        if message_content_edit_limit_seconds is None:
            message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds
        if allow_community_topic_editing is None:
            allow_community_topic_editing = realm.allow_community_topic_editing
        do_set_realm_message_editing(realm,
                                     allow_message_editing,
                                     message_content_edit_limit_seconds,
                                     allow_community_topic_editing,
                                     acting_user=user_profile)
        data['allow_message_editing'] = allow_message_editing
        data[
            'message_content_edit_limit_seconds'] = message_content_edit_limit_seconds
        data['allow_community_topic_editing'] = allow_community_topic_editing

    # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean,
    # str or integer field, and thus doesn't fit into the do_set_realm_property framework.
    if notifications_stream_id is not None:
        if realm.notifications_stream is None or (realm.notifications_stream.id
                                                  != notifications_stream_id):
            new_notifications_stream = None
            if notifications_stream_id >= 0:
                (new_notifications_stream, recipient,
                 sub) = access_stream_by_id(user_profile,
                                            notifications_stream_id)
            do_set_realm_notifications_stream(realm,
                                              new_notifications_stream,
                                              notifications_stream_id,
                                              acting_user=user_profile)
            data['notifications_stream_id'] = notifications_stream_id

    if signup_notifications_stream_id is not None:
        if realm.signup_notifications_stream is None or (
                realm.signup_notifications_stream.id !=
                signup_notifications_stream_id):
            new_signup_notifications_stream = None
            if signup_notifications_stream_id >= 0:
                (new_signup_notifications_stream, recipient,
                 sub) = access_stream_by_id(user_profile,
                                            signup_notifications_stream_id)
            do_set_realm_signup_notifications_stream(
                realm,
                new_signup_notifications_stream,
                signup_notifications_stream_id,
                acting_user=user_profile)
            data[
                'signup_notifications_stream_id'] = signup_notifications_stream_id

    if default_code_block_language is not None:
        # Migrate '', used in the API to encode the default/None behavior of this feature.
        if default_code_block_language == '':
            data['default_code_block_language'] = None
        else:
            data['default_code_block_language'] = default_code_block_language

    return json_success(data)
示例#13
0
def patch_bot_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    bot_id: int,
    full_name: Optional[str] = REQ(default=None),
    role: Optional[int] = REQ(
        default=None,
        json_validator=check_int_in(UserProfile.ROLE_TYPES, ),
    ),
    bot_owner_id: Optional[int] = REQ(json_validator=check_int, default=None),
    config_data: Optional[Dict[str, str]] = REQ(
        default=None, json_validator=check_dict(value_validator=check_string)),
    service_payload_url: Optional[str] = REQ(json_validator=check_url,
                                             default=None),
    service_interface: int = REQ(json_validator=check_int, default=1),
    default_sending_stream: Optional[str] = REQ(default=None),
    default_events_register_stream: Optional[str] = REQ(default=None),
    default_all_public_streams: Optional[bool] = REQ(
        default=None, json_validator=check_bool),
) -> HttpResponse:
    bot = access_bot_by_id(user_profile, bot_id)

    if full_name is not None:
        check_change_bot_full_name(bot, full_name, user_profile)

    if role is not None and bot.role != role:
        # Logic duplicated from update_user_backend.
        if UserProfile.ROLE_REALM_OWNER in [
                role, bot.role
        ] and not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()

        do_change_user_role(bot, role, acting_user=user_profile)

    if bot_owner_id is not None:
        try:
            owner = get_user_profile_by_id_in_realm(bot_owner_id,
                                                    user_profile.realm)
        except UserProfile.DoesNotExist:
            raise JsonableError(_("Failed to change owner, no such user"))
        if not owner.is_active:
            raise JsonableError(
                _("Failed to change owner, user is deactivated"))
        if owner.is_bot:
            raise JsonableError(
                _("Failed to change owner, bots can't own other bots"))

        previous_owner = bot.bot_owner
        if previous_owner != owner:
            do_change_bot_owner(bot, owner, user_profile)

    if default_sending_stream is not None:
        if default_sending_stream == "":
            stream: Optional[Stream] = None
        else:
            (stream, sub) = access_stream_by_name(user_profile,
                                                  default_sending_stream)
        do_change_default_sending_stream(bot, stream, acting_user=user_profile)
    if default_events_register_stream is not None:
        if default_events_register_stream == "":
            stream = None
        else:
            (stream,
             sub) = access_stream_by_name(user_profile,
                                          default_events_register_stream)
        do_change_default_events_register_stream(bot,
                                                 stream,
                                                 acting_user=user_profile)
    if default_all_public_streams is not None:
        do_change_default_all_public_streams(bot,
                                             default_all_public_streams,
                                             acting_user=user_profile)

    if service_payload_url is not None:
        check_valid_interface_type(service_interface)
        assert service_interface is not None
        do_update_outgoing_webhook_service(bot, service_interface,
                                           service_payload_url)

    if config_data is not None:
        do_update_bot_config_data(bot, config_data)

    if len(request.FILES) == 0:
        pass
    elif len(request.FILES) == 1:
        user_file = list(request.FILES.values())[0]
        assert isinstance(user_file, UploadedFile)
        assert user_file.size is not None
        upload_avatar_image(user_file, user_profile, bot)
        avatar_source = UserProfile.AVATAR_FROM_USER
        do_change_avatar_fields(bot, avatar_source, acting_user=user_profile)
    else:
        raise JsonableError(_("You may only upload one file at a time"))

    json_result = dict(
        full_name=bot.full_name,
        avatar_url=avatar_url(bot),
        service_interface=service_interface,
        service_payload_url=service_payload_url,
        config_data=config_data,
        default_sending_stream=get_stream_name(bot.default_sending_stream),
        default_events_register_stream=get_stream_name(
            bot.default_events_register_stream),
        default_all_public_streams=bot.default_all_public_streams,
    )

    # Don't include the bot owner in case it is not set.
    # Default bots have no owner.
    if bot.bot_owner is not None:
        json_result["bot_owner"] = bot.bot_owner.email

    return json_success(request, data=json_result)