events: Check last_event_id for validity.

This verifies that the client passed a last_event_id that actually
came from the queue instead of making up an ID from the future.  It
turns out one of our tests was making up such an ID, but legitimate
clients are expected not to do so.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
This commit is contained in:
Anders Kaseorg 2019-07-25 13:06:59 -07:00 committed by Tim Abbott
parent bbbea9ec87
commit e00d4be6d5
2 changed files with 7 additions and 1 deletions

View File

@ -117,7 +117,7 @@ class EventsTestCase(TornadoWebTestCase):
event_queue_id = self.create_queue()
data = {
'queue_id': event_queue_id,
'last_event_id': 0,
'last_event_id': -1,
}
path = '/json/events?{}'.format(urllib.parse.urlencode(data))

View File

@ -240,6 +240,7 @@ class EventQueue:
def __init__(self, id: str) -> None:
self.queue = deque() # type: ignore # Should be Deque[Dict[str, Any]], but Deque isn't available in Python 3.4
self.next_event_id = 0 # type: int
self.newest_pruned_id = -1 # type: int
self.id = id # type: str
self.virtual_events = {} # type: Dict[str, Dict[str, Any]]
@ -295,6 +296,7 @@ class EventQueue:
# See the comment on pop; that applies here as well
def prune(self, through_id: int) -> None:
while len(self.queue) != 0 and self.queue[0]['id'] <= through_id:
self.newest_pruned_id = self.queue[0]['id']
self.pop()
def contents(self) -> List[Dict[str, Any]]:
@ -522,7 +524,11 @@ def fetch_events(query: Mapping[str, Any]) -> Dict[str, Any]:
raise BadEventQueueIdError(queue_id)
if user_profile_id != client.user_profile_id:
raise JsonableError(_("You are not authorized to get events from this queue"))
if last_event_id < client.event_queue.newest_pruned_id:
raise JsonableError(_("An event newer than %s has already been pruned!") % (last_event_id,))
client.event_queue.prune(last_event_id)
if last_event_id != client.event_queue.newest_pruned_id:
raise JsonableError(_("Event %s was not in this queue") % (last_event_id,))
was_connected = client.finish_current_handler()
if not client.event_queue.empty() or dont_block: