Esempio n. 1
0
def get_topics_backend(
    request: HttpRequest,
    maybe_user_profile: Union[UserProfile, AnonymousUser],
    stream_id: int = REQ(converter=to_non_negative_int, path_only=True),
) -> HttpResponse:

    if not maybe_user_profile.is_authenticated:
        is_web_public_query = True
        user_profile: Optional[UserProfile] = None
    else:
        is_web_public_query = False
        assert isinstance(maybe_user_profile, UserProfile)
        user_profile = maybe_user_profile
        assert user_profile is not None

    if is_web_public_query:
        realm = get_valid_realm_from_request(request)
        stream = access_web_public_stream(stream_id, realm)
        result = get_topic_history_for_public_stream(
            recipient_id=stream.recipient_id)

    else:
        assert user_profile is not None

        (stream, sub) = access_stream_by_id(user_profile, stream_id)

        result = get_topic_history_for_stream(
            user_profile=user_profile,
            recipient_id=stream.recipient_id,
            public_history=stream.is_history_public_to_subscribers(),
        )

    return json_success(dict(topics=result))
Esempio n. 2
0
def report_unnarrow_times(request: HttpRequest, user_profile: Union[UserProfile, AnonymousUser],
                          initial_core: int=REQ(converter=to_non_negative_int),
                          initial_free: int=REQ(converter=to_non_negative_int)) -> HttpResponse:
    request._log_data["extra"] = f"[{initial_core}ms/{initial_free}ms]"
    realm = get_valid_realm_from_request(request)
    base_key = statsd_key(realm.string_id, clean_periods=True)
    statsd.timing(f"unnarrow.initial_core.{base_key}", initial_core)
    statsd.timing(f"unnarrow.initial_free.{base_key}", initial_free)
    return json_success()
Esempio n. 3
0
def avatar(
    request: HttpRequest,
    maybe_user_profile: Union[UserProfile, AnonymousUser],
    email_or_id: str,
    medium: bool = False,
) -> HttpResponse:
    """Accepts an email address or user ID and returns the avatar"""
    is_email = False
    try:
        int(email_or_id)
    except ValueError:
        is_email = True

    if not maybe_user_profile.is_authenticated:
        # Allow anonynous access to avatars only if spectators are
        # enabled in the organization.
        realm = get_valid_realm_from_request(request)
        # TODO: Replace with realm.allow_web_public_streams_access()
        # when the method is available.
        if not realm.has_web_public_streams():
            raise MissingAuthenticationError()

        # We only allow the ID format for accessing a user's avatar
        # for spectators. This is mainly for defense in depth, since
        # email_address_visibility should mean spectators only
        # interact with fake email addresses anyway.
        if is_email:
            raise MissingAuthenticationError()
    else:
        realm = maybe_user_profile.realm

    try:
        if is_email:
            avatar_user_profile = get_user_including_cross_realm(
                email_or_id, realm)
        else:
            avatar_user_profile = get_user_by_id_in_realm_including_cross_realm(
                int(email_or_id), realm)
        # If there is a valid user account passed in, use its avatar
        url = avatar_url(avatar_user_profile, medium=medium)
    except UserProfile.DoesNotExist:
        # If there is no such user, treat it as a new gravatar
        email = email_or_id
        avatar_version = 1
        url = get_gravatar_url(email, avatar_version, medium)

    # We can rely on the URL already having query parameters. Because
    # our templates depend on being able to use the ampersand to
    # add query parameters to our url, get_avatar_url does '?x=x'
    # hacks to prevent us from having to jump through decode/encode hoops.
    assert url is not None
    url = append_url_query_string(url, request.META["QUERY_STRING"])
    return redirect(url)
Esempio n. 4
0
def json_fetch_raw_message(
    request: HttpRequest,
    maybe_user_profile: Union[UserProfile, AnonymousUser],
    message_id: int = REQ(converter=to_non_negative_int, path_only=True),
) -> HttpResponse:

    if not maybe_user_profile.is_authenticated:
        realm = get_valid_realm_from_request(request)
        message = access_web_public_message(realm, message_id)
    else:
        (message, user_message) = access_message(maybe_user_profile,
                                                 message_id)

    return json_success(request, data={"raw_content": message.content})
Esempio n. 5
0
def report_narrow_times(
        request: HttpRequest,
        user_profile: Union[UserProfile, AnonymousUser],
        initial_core: int = REQ(converter=to_non_negative_int),
        initial_free: int = REQ(converter=to_non_negative_int),
        network: int = REQ(converter=to_non_negative_int),
) -> HttpResponse:
    log_data = RequestNotes.get_notes(request).log_data
    assert log_data is not None
    log_data["extra"] = f"[{initial_core}ms/{initial_free}ms/{network}ms]"
    realm = get_valid_realm_from_request(request)
    base_key = statsd_key(realm.string_id, clean_periods=True)
    statsd.timing(f"narrow.initial_core.{base_key}", initial_core)
    statsd.timing(f"narrow.initial_free.{base_key}", initial_free)
    statsd.timing(f"narrow.network.{base_key}", network)
    return json_success(request)
Esempio n. 6
0
def backend_serve_thumbnail(
        request: HttpRequest,
        maybe_user_profile: Union[UserProfile, AnonymousUser],
        url: str = REQ(),
        size_requested: str = REQ("size"),
) -> HttpResponse:
    if not maybe_user_profile.is_authenticated:
        realm = get_valid_realm_from_request(request)
    else:
        assert isinstance(maybe_user_profile, UserProfile)
        realm = maybe_user_profile.realm

    if not validate_thumbnail_request(realm, maybe_user_profile, url):
        return HttpResponseForbidden(
            _("<p>You are not authorized to view this file.</p>"))

    thumbnail_url = generate_thumbnail_url(url)
    return redirect(thumbnail_url)
Esempio n. 7
0
def home(request: HttpRequest) -> HttpResponse:
    subdomain = get_subdomain(request)

    # If settings.ROOT_DOMAIN_LANDING_PAGE and this is the root
    # domain, send the user the landing page.
    if settings.ROOT_DOMAIN_LANDING_PAGE and subdomain == Realm.SUBDOMAIN_FOR_ROOT_DOMAIN:
        return hello_view(request)

    # TODO: The following logic is a bit hard to read. We save a
    # database query in the common case by avoiding the call to
    # `get_valid_realm_from_request` if user hasn't requested
    # web-public access.
    if (request.POST.get("prefers_web_public_view") == "true"
            or request.session.get("prefers_web_public_view")
        ) and get_valid_realm_from_request(
            request).allow_web_public_streams_access():
        return web_public_view(home_real)(request)
    return zulip_login_required(home_real)(request)
Esempio n. 8
0
def json_fetch_raw_message(
    request: HttpRequest,
    maybe_user_profile: Union[UserProfile, AnonymousUser],
    message_id: int = REQ(converter=to_non_negative_int, path_only=True),
    apply_markdown: bool = REQ(json_validator=check_bool, default=True),
) -> HttpResponse:

    if not maybe_user_profile.is_authenticated:
        realm = get_valid_realm_from_request(request)
        message = access_web_public_message(realm, message_id)
    else:
        (message, user_message) = access_message(maybe_user_profile,
                                                 message_id)

    flags = ["read"]
    if not maybe_user_profile.is_authenticated:
        allow_edit_history = realm.allow_edit_history
    else:
        if user_message:
            flags = user_message.flags_list()
        else:
            flags = ["read", "historical"]
        allow_edit_history = maybe_user_profile.realm.allow_edit_history

    # Security note: It's important that we call this only with a
    # message already fetched via `access_message` type methods,
    # as we do above.
    message_dict_list = messages_for_ids(
        message_ids=[message.id],
        user_message_flags={message_id: flags},
        search_fields={},
        apply_markdown=apply_markdown,
        client_gravatar=True,
        allow_edit_history=allow_edit_history,
    )
    response = dict(
        message=message_dict_list[0],
        # raw_content is deprecated; we will need to wait until
        # clients have been fully migrated to using the modern API
        # before removing this, probably in 2023.
        raw_content=message.content,
    )
    return json_success(request, response)
Esempio n. 9
0
def serve_file(
    request: HttpRequest,
    maybe_user_profile: Union[UserProfile, AnonymousUser],
    realm_id_str: str,
    filename: str,
    url_only: bool = False,
    download: bool = False,
) -> HttpResponse:
    path_id = f"{realm_id_str}/{filename}"
    realm = get_valid_realm_from_request(request)
    is_authorized = validate_attachment_request(maybe_user_profile, path_id, realm)

    if is_authorized is None:
        return HttpResponseNotFound(_("<p>File not found.</p>"))
    if not is_authorized:
        return HttpResponseForbidden(_("<p>You are not authorized to view this file.</p>"))
    if settings.LOCAL_UPLOADS_DIR is not None:
        return serve_local(request, path_id, url_only, download=download)

    return serve_s3(request, path_id, url_only, download=download)
Esempio n. 10
0
File: home.py Progetto: dcfix/zulip
def home_real(request: HttpRequest) -> HttpResponse:
    # Before we do any real work, check if the app is banned.
    client_user_agent = request.META.get("HTTP_USER_AGENT", "")
    (insecure_desktop_app, banned_desktop_app,
     auto_update_broken) = is_outdated_desktop_app(client_user_agent)
    if banned_desktop_app:
        return render(
            request,
            "zerver/insecure_desktop_app.html",
            context={
                "auto_update_broken": auto_update_broken,
            },
        )
    (unsupported_browser,
     browser_name) = is_unsupported_browser(client_user_agent)
    if unsupported_browser:
        return render(
            request,
            "zerver/unsupported_browser.html",
            context={
                "browser_name": browser_name,
            },
        )

    # We need to modify the session object every two weeks or it will expire.
    # This line makes reloading the page a sufficient action to keep the
    # session alive.
    request.session.modified = True

    if request.user.is_authenticated:
        user_profile = request.user
        realm = user_profile.realm
    else:
        # user_profile=None corresponds to the logged-out "web_public" visitor case.
        user_profile = None
        realm = get_valid_realm_from_request(request)

    update_last_reminder(user_profile)

    statsd.incr("views.home")

    # If a user hasn't signed the current Terms of Service, send them there
    if need_accept_tos(user_profile):
        return accounts_accept_terms(request)

    narrow, narrow_stream, narrow_topic = detect_narrowed_window(
        request, user_profile)

    if user_profile is not None:
        first_in_realm = realm_user_count(user_profile.realm) == 1
        # If you are the only person in the realm and you didn't invite
        # anyone, we'll continue to encourage you to do so on the frontend.
        prompt_for_invites = (first_in_realm
                              and not PreregistrationUser.objects.filter(
                                  referred_by=user_profile).count())
        needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING

    else:
        first_in_realm = False
        prompt_for_invites = False
        # The current tutorial doesn't super make sense for logged-out users.
        needs_tutorial = False

    queue_id, page_params = build_page_params_for_home_page_load(
        request=request,
        user_profile=user_profile,
        realm=realm,
        insecure_desktop_app=insecure_desktop_app,
        narrow=narrow,
        narrow_stream=narrow_stream,
        narrow_topic=narrow_topic,
        first_in_realm=first_in_realm,
        prompt_for_invites=prompt_for_invites,
        needs_tutorial=needs_tutorial,
    )

    show_invites, show_add_streams = compute_show_invites_and_add_streams(
        user_profile)

    request._log_data["extra"] = "[{}]".format(queue_id)

    csp_nonce = secrets.token_hex(24)

    user_permission_info = get_user_permission_info(user_profile)

    response = render(
        request,
        "zerver/app/index.html",
        context={
            "user_profile": user_profile,
            "page_params": page_params,
            "csp_nonce": csp_nonce,
            "search_pills_enabled": settings.SEARCH_PILLS_ENABLED,
            "show_invites": show_invites,
            "show_add_streams": show_add_streams,
            "is_owner": user_permission_info.is_realm_owner,
            "is_admin": user_permission_info.is_realm_admin,
            "is_guest": user_permission_info.is_guest,
            "color_scheme": user_permission_info.color_scheme,
            "embedded": narrow_stream is not None,
            "max_file_upload_size_mib": settings.MAX_FILE_UPLOAD_SIZE,
        },
    )
    patch_cache_control(response,
                        no_cache=True,
                        no_store=True,
                        must_revalidate=True)
    return response
Esempio n. 11
0
def events_register_backend(
    request: HttpRequest,
    maybe_user_profile: Union[UserProfile, AnonymousUser],
    apply_markdown: bool = REQ(default=False, json_validator=check_bool),
    client_gravatar: bool = REQ(default=True, json_validator=check_bool),
    slim_presence: bool = REQ(default=False, json_validator=check_bool),
    all_public_streams: Optional[bool] = REQ(default=None, json_validator=check_bool),
    include_subscribers: bool = REQ(default=False, json_validator=check_bool),
    client_capabilities: Optional[Dict[str, bool]] = REQ(
        json_validator=check_dict(
            [
                # This field was accidentally made required when it was added in v2.0.0-781;
                # this was not realized until after the release of Zulip 2.1.2. (It remains
                # required to help ensure backwards compatibility of client code.)
                ("notification_settings_null", check_bool),
            ],
            [
                # Any new fields of `client_capabilities` should be optional. Add them here.
                ("bulk_message_deletion", check_bool),
                ("user_avatar_url_field_optional", check_bool),
                ("stream_typing_notifications", check_bool),
                ("user_settings_object", check_bool),
            ],
            value_validator=check_bool,
        ),
        default=None,
    ),
    event_types: Optional[Sequence[str]] = REQ(
        json_validator=check_list(check_string), default=None
    ),
    fetch_event_types: Optional[Sequence[str]] = REQ(
        json_validator=check_list(check_string), default=None
    ),
    narrow: NarrowT = REQ(
        json_validator=check_list(check_list(check_string, length=2)), default=[]
    ),
    queue_lifespan_secs: int = REQ(json_validator=check_int, default=0, documentation_pending=True),
) -> HttpResponse:
    if maybe_user_profile.is_authenticated:
        user_profile = maybe_user_profile
        assert isinstance(user_profile, UserProfile)
        realm = user_profile.realm

        if all_public_streams and not user_profile.can_access_public_streams():
            raise JsonableError(_("User not authorized for this query"))

        all_public_streams = _default_all_public_streams(user_profile, all_public_streams)
        narrow = _default_narrow(user_profile, narrow)
    else:
        user_profile = None
        realm = get_valid_realm_from_request(request)

        if not realm.allow_web_public_streams_access():
            raise MissingAuthenticationError()

        all_public_streams = False

    if client_capabilities is None:
        client_capabilities = {}

    client = RequestNotes.get_notes(request).client
    assert client is not None

    ret = do_events_register(
        user_profile,
        realm,
        client,
        apply_markdown,
        client_gravatar,
        slim_presence,
        event_types,
        queue_lifespan_secs,
        all_public_streams,
        narrow=narrow,
        include_subscribers=include_subscribers,
        client_capabilities=client_capabilities,
        fetch_event_types=fetch_event_types,
    )
    return json_success(request, data=ret)
Esempio n. 12
0
def home_real(request: HttpRequest) -> HttpResponse:
    # Before we do any real work, check if the app is banned.
    client_user_agent = request.META.get("HTTP_USER_AGENT", "")
    (insecure_desktop_app, banned_desktop_app,
     auto_update_broken) = is_outdated_desktop_app(client_user_agent)
    if banned_desktop_app:
        return render(
            request,
            "zerver/insecure_desktop_app.html",
            context={
                "auto_update_broken": auto_update_broken,
            },
        )
    (unsupported_browser,
     browser_name) = is_unsupported_browser(client_user_agent)
    if unsupported_browser:
        return render(
            request,
            "zerver/unsupported_browser.html",
            context={
                "browser_name": browser_name,
            },
        )

    # We need to modify the session object every two weeks or it will expire.
    # This line makes reloading the page a sufficient action to keep the
    # session alive.
    request.session.modified = True

    if request.user.is_authenticated:
        user_profile = request.user
        realm = user_profile.realm

        # User is logged in and hence no longer `prefers_web_public_view`.
        if "prefers_web_public_view" in request.session.keys():
            del request.session["prefers_web_public_view"]
    else:
        realm = get_valid_realm_from_request(request)

        # TODO: Ideally, we'd open Zulip directly as a spectator if
        # the URL had clicked a link to content on a web-public
        # stream.  We could maybe do this by parsing `next`, but it's
        # not super convenient with Zulip's hash-based URL scheme.

        # The "Access without an account" button on the login page
        # submits a POST to this page with this hidden field set.
        if request.POST.get("prefers_web_public_view") == "true":
            request.session["prefers_web_public_view"] = True
            # We serve a redirect here, rather than serving a page, to
            # avoid browser "Confirm form resubmission" prompts on reload.
            redirect_to = get_safe_redirect_to(request.POST.get("next"),
                                               realm.uri)
            return redirect(redirect_to)

        prefers_web_public_view = request.session.get(
            "prefers_web_public_view")
        if not prefers_web_public_view:
            # For users who haven't opted into the spectator
            # experience, we redirect to the login page.
            return zulip_redirect_to_login(request,
                                           settings.HOME_NOT_LOGGED_IN)

        # For users who have selected public access, we load the
        # spectator experience.  We fall through to the shared code
        # for loading the application, with user_profile=None encoding
        # that we're a spectator, not a logged-in user.
        user_profile = None

    update_last_reminder(user_profile)

    statsd.incr("views.home")

    # If a user hasn't signed the current Terms of Service, send them there
    if need_accept_tos(user_profile):
        return accounts_accept_terms(request)

    narrow, narrow_stream, narrow_topic = detect_narrowed_window(
        request, user_profile)

    if user_profile is not None:
        first_in_realm = realm_user_count(user_profile.realm) == 1
        # If you are the only person in the realm and you didn't invite
        # anyone, we'll continue to encourage you to do so on the frontend.
        prompt_for_invites = (first_in_realm
                              and not PreregistrationUser.objects.filter(
                                  referred_by=user_profile).count())
        needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING

    else:
        first_in_realm = False
        prompt_for_invites = False
        # The current tutorial doesn't super make sense for logged-out users.
        needs_tutorial = False

    queue_id, page_params = build_page_params_for_home_page_load(
        request=request,
        user_profile=user_profile,
        realm=realm,
        insecure_desktop_app=insecure_desktop_app,
        narrow=narrow,
        narrow_stream=narrow_stream,
        narrow_topic=narrow_topic,
        first_in_realm=first_in_realm,
        prompt_for_invites=prompt_for_invites,
        needs_tutorial=needs_tutorial,
    )

    log_data = RequestNotes.get_notes(request).log_data
    assert log_data is not None
    log_data["extra"] = f"[{queue_id}]"

    csp_nonce = secrets.token_hex(24)

    user_permission_info = get_user_permission_info(user_profile)

    response = render(
        request,
        "zerver/app/index.html",
        context={
            "user_profile": user_profile,
            "page_params": page_params,
            "csp_nonce": csp_nonce,
            "color_scheme": user_permission_info.color_scheme,
        },
    )
    patch_cache_control(response,
                        no_cache=True,
                        no_store=True,
                        must_revalidate=True)
    return response
Esempio n. 13
0
def home_real(request: HttpRequest) -> HttpResponse:
    # Before we do any real work, check if the app is banned.
    client_user_agent = request.headers.get("User-Agent", "")
    (insecure_desktop_app, banned_desktop_app,
     auto_update_broken) = is_outdated_desktop_app(client_user_agent)
    if banned_desktop_app:
        return render(
            request,
            "zerver/insecure_desktop_app.html",
            context={
                "auto_update_broken": auto_update_broken,
            },
        )
    (unsupported_browser,
     browser_name) = is_unsupported_browser(client_user_agent)
    if unsupported_browser:
        return render(
            request,
            "zerver/unsupported_browser.html",
            context={
                "browser_name": browser_name,
            },
        )

    # We need to modify the session object every two weeks or it will expire.
    # This line makes reloading the page a sufficient action to keep the
    # session alive.
    request.session.modified = True

    if request.user.is_authenticated:
        user_profile = request.user
        realm = user_profile.realm
    else:
        realm = get_valid_realm_from_request(request)
        # We load the spectator experience.  We fall through to the shared code
        # for loading the application, with user_profile=None encoding
        # that we're a spectator, not a logged-in user.
        user_profile = None

    update_last_reminder(user_profile)

    statsd.incr("views.home")

    # If a user hasn't signed the current Terms of Service, send them there
    if need_accept_tos(user_profile):
        return accounts_accept_terms(request)

    narrow, narrow_stream, narrow_topic = detect_narrowed_window(
        request, user_profile)

    if user_profile is not None:
        first_in_realm = realm_user_count(user_profile.realm) == 1
        # If you are the only person in the realm and you didn't invite
        # anyone, we'll continue to encourage you to do so on the frontend.
        prompt_for_invites = (first_in_realm
                              and not PreregistrationUser.objects.filter(
                                  referred_by=user_profile).count())
        needs_tutorial = user_profile.tutorial_status == UserProfile.TUTORIAL_WAITING

    else:
        first_in_realm = False
        prompt_for_invites = False
        # The current tutorial doesn't super make sense for logged-out users.
        needs_tutorial = False

    queue_id, page_params = build_page_params_for_home_page_load(
        request=request,
        user_profile=user_profile,
        realm=realm,
        insecure_desktop_app=insecure_desktop_app,
        narrow=narrow,
        narrow_stream=narrow_stream,
        narrow_topic=narrow_topic,
        first_in_realm=first_in_realm,
        prompt_for_invites=prompt_for_invites,
        needs_tutorial=needs_tutorial,
    )

    log_data = RequestNotes.get_notes(request).log_data
    assert log_data is not None
    log_data["extra"] = f"[{queue_id}]"

    csp_nonce = secrets.token_hex(24)

    user_permission_info = get_user_permission_info(user_profile)

    response = render(
        request,
        "zerver/app/index.html",
        context={
            "user_profile": user_profile,
            "page_params": page_params,
            "csp_nonce": csp_nonce,
            "color_scheme": user_permission_info.color_scheme,
        },
    )
    patch_cache_control(response,
                        no_cache=True,
                        no_store=True,
                        must_revalidate=True)
    return response