message_fetch: Move ok_to_include_history to zerver.lib.narrow.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2022-10-18 19:14:36 -07:00 committed by Tim Abbott
parent e797715f1d
commit 4eea5e102e
3 changed files with 54 additions and 52 deletions

View File

@ -41,6 +41,8 @@ from zerver.lib.addressee import get_user_profiles, get_user_profiles_by_ids
from zerver.lib.exceptions import ErrorCode, JsonableError from zerver.lib.exceptions import ErrorCode, JsonableError
from zerver.lib.recipient_users import recipient_for_user_profiles from zerver.lib.recipient_users import recipient_for_user_profiles
from zerver.lib.streams import ( from zerver.lib.streams import (
can_access_stream_history_by_id,
can_access_stream_history_by_name,
get_public_streams_queryset, get_public_streams_queryset,
get_stream_by_narrow_operand_access_unchecked, get_stream_by_narrow_operand_access_unchecked,
get_web_public_streams_queryset, get_web_public_streams_queryset,
@ -704,6 +706,56 @@ def narrow_parameter(var_name: str, json: str) -> OptionalNarrowListT:
return list(map(convert_term, data)) return list(map(convert_term, data))
def ok_to_include_history(
narrow: OptionalNarrowListT, user_profile: Optional[UserProfile], is_web_public_query: bool
) -> bool:
# There are occasions where we need to find Message rows that
# have no corresponding UserMessage row, because the user is
# reading a public stream that might include messages that
# were sent while the user was not subscribed, but which they are
# allowed to see. We have to be very careful about constructing
# queries in those situations, so this function should return True
# only if we are 100% sure that we're gonna add a clause to the
# query that narrows to a particular public stream on the user's realm.
# If we screw this up, then we can get into a nasty situation of
# polluting our narrow results with messages from other realms.
# For web-public queries, we are always returning history. The
# analogues of the below stream access checks for whether streams
# have is_web_public set and banning is operators in this code
# path are done directly in NarrowBuilder.
if is_web_public_query:
assert user_profile is None
return True
assert user_profile is not None
include_history = False
if narrow is not None:
for term in narrow:
if term["operator"] == "stream" and not term.get("negated", False):
operand: Union[str, int] = term["operand"]
if isinstance(operand, str):
include_history = can_access_stream_history_by_name(user_profile, operand)
else:
include_history = can_access_stream_history_by_id(user_profile, operand)
elif (
term["operator"] == "streams"
and term["operand"] == "public"
and not term.get("negated", False)
and user_profile.can_access_public_streams()
):
include_history = True
# Disable historical messages if the user is narrowing on anything
# that's a property on the UserMessage table. There cannot be
# historical messages in these cases anyway.
for term in narrow:
if term["operator"] == "is":
include_history = False
return include_history
def get_stream_from_narrow_access_unchecked( def get_stream_from_narrow_access_unchecked(
narrow: OptionalNarrowListT, realm: Realm narrow: OptionalNarrowListT, realm: Realm
) -> Optional[Stream]: ) -> Optional[Stream]:

View File

@ -32,6 +32,7 @@ from zerver.lib.narrow import (
build_narrow_filter, build_narrow_filter,
exclude_muting_conditions, exclude_muting_conditions,
is_spectator_compatible, is_spectator_compatible,
ok_to_include_history,
) )
from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection
from zerver.lib.streams import StreamDict, create_streams_if_needed, get_public_streams_queryset from zerver.lib.streams import StreamDict, create_streams_if_needed, get_public_streams_queryset
@ -57,7 +58,6 @@ from zerver.views.message_fetch import (
LARGER_THAN_MAX_MESSAGE_ID, LARGER_THAN_MAX_MESSAGE_ID,
find_first_unread_anchor, find_first_unread_anchor,
get_messages_backend, get_messages_backend,
ok_to_include_history,
post_process_limited_query, post_process_limited_query,
) )

View File

@ -32,11 +32,11 @@ from zerver.lib.narrow import (
is_spectator_compatible, is_spectator_compatible,
is_web_public_narrow, is_web_public_narrow,
narrow_parameter, narrow_parameter,
ok_to_include_history,
) )
from zerver.lib.request import REQ, RequestNotes, has_request_variables from zerver.lib.request import REQ, RequestNotes, has_request_variables
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection from zerver.lib.sqlalchemy_utils import get_sqlalchemy_connection
from zerver.lib.streams import can_access_stream_history_by_id, can_access_stream_history_by_name
from zerver.lib.topic import DB_TOPIC_NAME, MATCH_TOPIC, topic_column_sa from zerver.lib.topic import DB_TOPIC_NAME, MATCH_TOPIC, topic_column_sa
from zerver.lib.utils import statsd from zerver.lib.utils import statsd
from zerver.lib.validator import check_bool, check_int, check_list, to_non_negative_int from zerver.lib.validator import check_bool, check_int, check_list, to_non_negative_int
@ -95,56 +95,6 @@ def get_search_fields(
} }
def ok_to_include_history(
narrow: OptionalNarrowListT, user_profile: Optional[UserProfile], is_web_public_query: bool
) -> bool:
# There are occasions where we need to find Message rows that
# have no corresponding UserMessage row, because the user is
# reading a public stream that might include messages that
# were sent while the user was not subscribed, but which they are
# allowed to see. We have to be very careful about constructing
# queries in those situations, so this function should return True
# only if we are 100% sure that we're gonna add a clause to the
# query that narrows to a particular public stream on the user's realm.
# If we screw this up, then we can get into a nasty situation of
# polluting our narrow results with messages from other realms.
# For web-public queries, we are always returning history. The
# analogues of the below stream access checks for whether streams
# have is_web_public set and banning is operators in this code
# path are done directly in NarrowBuilder.
if is_web_public_query:
assert user_profile is None
return True
assert user_profile is not None
include_history = False
if narrow is not None:
for term in narrow:
if term["operator"] == "stream" and not term.get("negated", False):
operand: Union[str, int] = term["operand"]
if isinstance(operand, str):
include_history = can_access_stream_history_by_name(user_profile, operand)
else:
include_history = can_access_stream_history_by_id(user_profile, operand)
elif (
term["operator"] == "streams"
and term["operand"] == "public"
and not term.get("negated", False)
and user_profile.can_access_public_streams()
):
include_history = True
# Disable historical messages if the user is narrowing on anything
# that's a property on the UserMessage table. There cannot be
# historical messages in these cases anyway.
for term in narrow:
if term["operator"] == "is":
include_history = False
return include_history
def find_first_unread_anchor( def find_first_unread_anchor(
sa_conn: Connection, user_profile: Optional[UserProfile], narrow: OptionalNarrowListT sa_conn: Connection, user_profile: Optional[UserProfile], narrow: OptionalNarrowListT
) -> int: ) -> int: