def cleanup_event_queue(request: HttpRequest, user_profile: UserProfile, queue_id: str=REQ()) -> HttpResponse: client = get_client_descriptor(str(queue_id)) if client is None: raise BadEventQueueIdError(queue_id) if user_profile.id != client.user_profile_id: return json_error(_("You are not authorized to access this queue")) request._log_data['extra'] = "[%s]" % (queue_id,) client.cleanup() return json_success()
def cleanup_event_queue(request, user_profile, queue_id=REQ()): # type: (HttpRequest, UserProfile, text_type) -> HttpResponse client = get_client_descriptor(str(queue_id)) if client is None: return json_error(_("Bad event queue id: %s") % (queue_id,)) if user_profile.id != client.user_profile_id: return json_error(_("You are not authorized to access this queue")) request._log_data['extra'] = "[%s]" % (queue_id,) client.cleanup() return json_success()
def cleanup_event_queue(request, user_profile, queue_id=REQ()): # type: (HttpRequest, UserProfile, text_type) -> HttpResponse client = get_client_descriptor(str(queue_id)) if client is None: return json_error(_("Bad event queue id: %s") % (queue_id, )) if user_profile.id != client.user_profile_id: return json_error(_("You are not authorized to access this queue")) request._log_data['extra'] = "[%s]" % (queue_id, ) client.cleanup() return json_success()
def allocate_event_queue() -> ClientDescriptor: result = self.tornado_call(get_events, user_profile, {"apply_markdown": ujson.dumps(True), "client_gravatar": ujson.dumps(True), "event_types": ujson.dumps(["message"]), "user_client": "website", "dont_block": ujson.dumps(True)}) self.assert_json_success(result) queue_id = ujson.loads(result.content)["queue_id"] return get_client_descriptor(queue_id)
def authenticate_client(self, msg: Dict[str, Any]) -> None: if self.authenticated: self.session.send_message({'req_id': msg['req_id'], 'type': 'response', 'response': {'result': 'error', 'msg': 'Already authenticated'}}) return user_profile = get_user_profile(self.browser_session_id) if user_profile is None: raise JsonableError(_('Unknown or missing session')) self.session.user_profile = user_profile if 'csrf_token' not in msg['request']: # Debugging code to help with understanding #6961 logging.error("CSRF token missing from websockets auth request: %s" % (msg['request'],)) raise JsonableError(_('CSRF token entry missing from request')) if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token): raise JsonableError(_('CSRF token does not match that in cookie')) if 'queue_id' not in msg['request']: raise JsonableError(_("Missing 'queue_id' argument")) queue_id = msg['request']['queue_id'] client = get_client_descriptor(queue_id) if client is None: raise BadEventQueueIdError(queue_id) if user_profile.id != client.user_profile_id: raise JsonableError(_("You are not the owner of the queue with id '%s'") % (queue_id,)) self.authenticated = True register_connection(queue_id, self) response = {'req_id': msg['req_id'], 'type': 'response', 'response': {'result': 'success', 'msg': ''}} status_inquiries = msg['request'].get('status_inquiries') if status_inquiries is not None: results = {} # type: Dict[str, Dict[str, str]] for inquiry in status_inquiries: status = redis_client.hgetall(req_redis_key(inquiry)) # type: Dict[bytes, bytes] if len(status) == 0: result = {'status': 'not_received'} elif b'response' not in status: result = {'status': status[b'status'].decode('utf-8')} else: result = {'status': status[b'status'].decode('utf-8'), 'response': ujson.loads(status[b'response'])} results[str(inquiry)] = result response['response']['status_inquiries'] = results self.session.send_message(response) ioloop = tornado.ioloop.IOLoop.instance() ioloop.remove_timeout(self.timeout_handle)
def authenticate_client(self, msg: Dict[str, Any]) -> None: if self.authenticated: self.session.send_message({'req_id': msg['req_id'], 'type': 'response', 'response': {'result': 'error', 'msg': 'Already authenticated'}}) return user_profile = get_user_profile(self.browser_session_id) if user_profile is None: raise JsonableError(_('Unknown or missing session')) self.session.user_profile = user_profile if 'csrf_token' not in msg['request']: # Debugging code to help with understanding #6961 logging.error("Invalid websockets auth request: %s" % (msg['request'],)) raise JsonableError(_('CSRF token entry missing from request')) if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token): raise JsonableError(_('CSRF token does not match that in cookie')) if 'queue_id' not in msg['request']: raise JsonableError(_("Missing 'queue_id' argument")) queue_id = msg['request']['queue_id'] client = get_client_descriptor(queue_id) if client is None: raise BadEventQueueIdError(queue_id) if user_profile.id != client.user_profile_id: raise JsonableError(_("You are not the owner of the queue with id '%s'") % (queue_id,)) self.authenticated = True register_connection(queue_id, self) response = {'req_id': msg['req_id'], 'type': 'response', 'response': {'result': 'success', 'msg': ''}} status_inquiries = msg['request'].get('status_inquiries') if status_inquiries is not None: results = {} # type: Dict[str, Dict[str, str]] for inquiry in status_inquiries: status = redis_client.hgetall(req_redis_key(inquiry)) # type: Dict[bytes, bytes] if len(status) == 0: result = {'status': 'not_received'} elif b'response' not in status: result = {'status': status[b'status'].decode('utf-8')} else: result = {'status': status[b'status'].decode('utf-8'), 'response': ujson.loads(status[b'response'])} results[str(inquiry)] = result response['response']['status_inquiries'] = results self.session.send_message(response) ioloop = tornado.ioloop.IOLoop.instance() ioloop.remove_timeout(self.timeout_handle)
def cleanup_event_queue( request: HttpRequest, user_profile: UserProfile, queue_id: str = REQ() ) -> HttpResponse: client = get_client_descriptor(str(queue_id)) if client is None: raise BadEventQueueIdError(queue_id) if user_profile.id != client.user_profile_id: raise JsonableError(_("You are not authorized to access this queue")) log_data = get_request_notes(request).log_data assert log_data is not None log_data["extra"] = f"[{queue_id}]" client.cleanup() return json_success()
def authenticate_client(self, msg): # type: (Dict[str, Any]) -> None if self.authenticated: self.session.send_message({'req_id': msg['req_id'], 'type': 'response', 'response': {'result': 'error', 'msg': 'Already authenticated'}}) return user_profile = get_user_profile(self.browser_session_id) if user_profile is None: raise SocketAuthError('Unknown or missing session') self.session.user_profile = user_profile if not _compare_salted_tokens(msg['request']['csrf_token'], self.csrf_token): raise SocketAuthError('CSRF token does not match that in cookie') if 'queue_id' not in msg['request']: raise SocketAuthError("Missing 'queue_id' argument") queue_id = msg['request']['queue_id'] client = get_client_descriptor(queue_id) if client is None: raise SocketAuthError('Bad event queue id: %s' % (queue_id,)) if user_profile.id != client.user_profile_id: raise SocketAuthError("You are not the owner of the queue with id '%s'" % (queue_id,)) self.authenticated = True register_connection(queue_id, self) response = {'req_id': msg['req_id'], 'type': 'response', 'response': {'result': 'success', 'msg': ''}} status_inquiries = msg['request'].get('status_inquiries') if status_inquiries is not None: results = {} for inquiry in status_inquiries: status = redis_client.hgetall(req_redis_key(inquiry)) if len(status) == 0: status['status'] = 'not_received' if 'response' in status: status['response'] = ujson.loads(status['response']) results[str(inquiry)] = status response['response']['status_inquiries'] = results self.session.send_message(response) ioloop = tornado.ioloop.IOLoop.instance() ioloop.remove_timeout(self.timeout_handle)
def test_end_to_end_missedmessage_hook(self) -> None: """Tests what arguments missedmessage_hook passes into maybe_enqueue_notifications. Combined with the previous test, this ensures that the missedmessage_hook is correct""" user_profile = self.example_user('hamlet') email = user_profile.email self.login(email) def change_subscription_properties( user_profile: UserProfile, stream: Stream, sub: Subscription, properties: Dict[str, bool]) -> None: for property_name, value in properties.items(): do_change_subscription_property(user_profile, sub, stream, property_name, value) result = self.tornado_call( get_events, user_profile, { "apply_markdown": ujson.dumps(True), "client_gravatar": ujson.dumps(True), "event_types": ujson.dumps(["message"]), "user_client": "website", "dont_block": ujson.dumps(True), }) self.assert_json_success(result) queue_id = ujson.loads(result.content)["queue_id"] client_descriptor = get_client_descriptor(queue_id) with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: # To test the missed_message hook, we first need to send a message msg_id = self.send_stream_message(self.example_email("iago"), "Denmark") # Verify that nothing happens if you call it as not the # "last client descriptor", in which case the function # short-circuits, since the `missedmessage_hook` handler # for garbage-collection is only for the user's last queue. missedmessage_hook(user_profile.id, client_descriptor, False) mock_enqueue.assert_not_called() # Now verify that we called the appropriate enqueue function missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, False, "Denmark", False, True, { 'email_notified': False, 'push_notified': False })) # Clear the event queue, before repeating with a private message client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_personal_message(self.example_email("iago"), email) with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, True, False, False, False, None, False, True, { 'email_notified': True, 'push_notified': True })) # Clear the event queue, now repeat with a mention client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_stream_message( self.example_email("iago"), "Denmark", content="@**King Hamlet** what's up?") with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, True, False, False, "Denmark", False, True, { 'email_notified': True, 'push_notified': True })) stream = get_stream("Denmark", user_profile.realm) sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM, recipient__type_id=stream.id) # Clear the event queue, now repeat with stream message with stream_push_notify change_subscription_properties(user_profile, stream, sub, {'push_notifications': True}) client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?") with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, True, False, "Denmark", False, True, { 'email_notified': False, 'push_notified': False })) # Clear the event queue, now repeat with stream message with stream_email_notify change_subscription_properties(user_profile, stream, sub, { 'push_notifications': False, 'email_notifications': True }) client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?") with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, True, "Denmark", False, True, { 'email_notified': False, 'push_notified': False })) # Clear the event queue, now repeat with stream message with stream_push_notify # on a muted topic, which we should not push notify for change_subscription_properties(user_profile, stream, sub, { 'push_notifications': True, 'email_notifications': False }) client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) do_mute_topic(user_profile, stream, sub.recipient, "mutingtest") msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?", topic_name="mutingtest") with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, False, "Denmark", False, True, { 'email_notified': False, 'push_notified': False })) # Clear the event queue, now repeat with stream message with stream_email_notify # on a muted stream, which we should not email notify for change_subscription_properties(user_profile, stream, sub, { 'push_notifications': False, 'email_notifications': True }) client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) change_subscription_properties(user_profile, stream, sub, {'in_home_view': False}) msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?") with mock.patch( "zerver.tornado.event_queue.maybe_enqueue_notifications" ) as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, False, "Denmark", False, True, { 'email_notified': False, 'push_notified': False })) # Clean up the state we just changed (not necessary unless we add more test code below) change_subscription_properties(user_profile, stream, sub, { 'push_notifications': True, 'in_home_view': True })
def test_end_to_end_missedmessage_hook(self): # type: () -> None """Tests what arguments missedmessage_hook passes into maybe_enqueue_notifications. Combined with the previous test, this ensures that the missedmessage_hook is correct""" user_profile = self.example_user('hamlet') email = user_profile.email self.login(email) result = self.tornado_call(get_events_backend, user_profile, {"apply_markdown": ujson.dumps(True), "event_types": ujson.dumps(["message"]), "user_client": "website", "dont_block": ujson.dumps(True), }) self.assert_json_success(result) queue_id = ujson.loads(result.content)["queue_id"] client_descriptor = get_client_descriptor(queue_id) with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # To test the missed_message hook, we first need to send a message msg_id = self.send_stream_message(self.example_email("iago"), "Denmark") # Verify that nothing happens if you call it as not the # "last client descriptor", in which case the function # short-circuits, since the `missedmessage_hook` handler # for garbage-collection is only for the user's last queue. missedmessage_hook(user_profile.id, client_descriptor, False) mock_enqueue.assert_not_called() # Now verify that we called the appropriate enqueue function missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, "Denmark", False, True, {'email_notified': False, 'push_notified': False})) # Clear the event queue, before repeating with a private message client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_personal_message(self.example_email("iago"), email) with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, True, False, False, None, False, True, {'email_notified': True, 'push_notified': True})) # Clear the event queue, now repeat with a mention client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="@**King Hamlet** what's up?") with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, True, False, "Denmark", False, True, {'email_notified': True, 'push_notified': True})) # Clear the event queue, now repeat with stream message with stream_push_notify stream = get_stream("Denmark", user_profile.realm) sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM, recipient__type_id=stream.id) sub.push_notifications = True sub.save() client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?") with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, True, "Denmark", False, True, {'email_notified': False, 'push_notified': False})) # Clear the event queue, now repeat with stream message with stream_push_notify # on a muted topic, which we should not push notify for client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) do_mute_topic(user_profile, stream, sub.recipient, "mutingtest") msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?", topic_name="mutingtest") with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, "Denmark", False, True, {'email_notified': False, 'push_notified': False})) # Clear the event queue, now repeat with stream message with stream_push_notify # on a muted stream, which we should not push notify for client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) sub.in_home_view = False sub.save() msg_id = self.send_stream_message(self.example_email("iago"), "Denmark", content="what's up everyone?") with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, "Denmark", False, True, {'email_notified': False, 'push_notified': False})) # Clean up the state we just changed (not necessary unless we add more test code below) sub.push_notifications = True sub.in_home_view = True sub.save()
def test_end_to_end_missedmessage_hook(self): # type: () -> None """Tests what arguments missedmessage_hook passes into maybe_enqueue_notifications. Combined with the previous test, this ensures that the missedmessage_hook is correct""" user_profile = self.example_user('hamlet') email = user_profile.email self.login(email) result = self.tornado_call(get_events_backend, user_profile, {"apply_markdown": ujson.dumps(True), "event_types": ujson.dumps(["message"]), "user_client": "website", "dont_block": ujson.dumps(True), }) self.assert_json_success(result) queue_id = ujson.loads(result.content)["queue_id"] client_descriptor = get_client_descriptor(queue_id) with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # To test the missed_message hook, we first need to send a message msg_id = self.send_message(self.example_email("iago"), "Denmark", Recipient.STREAM) # Verify that nothing happens if you call it as not the # "last client descriptor", in which case the function # short-circuits, since the `missedmessage_hook` handler # for garbage-collection is only for the user's last queue. missedmessage_hook(user_profile.id, client_descriptor, False) mock_enqueue.assert_not_called() # Now verify that we called the appropriate enqueue function missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, False, "Denmark", False, True)) # Clear the event queue, before repeating with a private message client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_message(self.example_email("iago"), [email], Recipient.PERSONAL) with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, True, False, False, None, False, True)) # Clear the event queue, now repeat with a mention client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_message(self.example_email("iago"), "Denmark", Recipient.STREAM, content="@**King Hamlet** what's up?") with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, True, False, "Denmark", False, True)) # Clear the event queue, now repeat with stream message with stream_push_notify stream = get_stream("Denmark", user_profile.realm) sub = Subscription.objects.get(user_profile=user_profile, recipient__type=Recipient.STREAM, recipient__type_id=stream.id) sub.push_notifications = True sub.save() client_descriptor.event_queue.pop() self.assertTrue(client_descriptor.event_queue.empty()) msg_id = self.send_message(self.example_email("iago"), "Denmark", Recipient.STREAM, content="what's up everyone?") with mock.patch("zerver.tornado.event_queue.maybe_enqueue_notifications") as mock_enqueue: # Clear the event queue, before repeating with a private message missedmessage_hook(user_profile.id, client_descriptor, True) mock_enqueue.assert_called_once() args_list = mock_enqueue.call_args_list[0][0] self.assertEqual(args_list, (user_profile.id, msg_id, False, False, True, "Denmark", False, True)) # Clean up the state sub.push_notifications = True sub.save()