def test_notify_bot_owner_on_invalid_json(self)-> None: @api_key_only_webhook_view('ClientName', notify_bot_owner_on_invalid_json=False) def my_webhook_raises_exception(request: HttpRequest, user_profile: UserProfile) -> None: raise InvalidJSONError("Malformed JSON") @api_key_only_webhook_view('ClientName', notify_bot_owner_on_invalid_json=True) def my_webhook(request: HttpRequest, user_profile: UserProfile) -> None: raise InvalidJSONError("Malformed JSON") webhook_bot_email = '*****@*****.**' webhook_bot_realm = get_realm('zulip') webhook_bot = get_user(webhook_bot_email, webhook_bot_realm) webhook_bot_api_key = get_api_key(webhook_bot) request = HostRequestMock() request.POST['api_key'] = webhook_bot_api_key request.host = "zulip.testserver" expected_msg = INVALID_JSON_MESSAGE.format(webhook_name='ClientName') last_message_id = self.get_last_message().id with self.assertRaisesRegex(JsonableError, "Malformed JSON"): my_webhook_raises_exception(request) # type: ignore # mypy doesn't seem to apply the decorator # First verify that without the setting, it doesn't send a PM to bot owner. msg = self.get_last_message() self.assertEqual(msg.id, last_message_id) self.assertNotEqual(msg.content, expected_msg.strip()) # Then verify that with the setting, it does send such a message. my_webhook(request) # type: ignore # mypy doesn't seem to apply the decorator msg = self.get_last_message() self.assertNotEqual(msg.id, last_message_id) self.assertEqual(msg.sender.email, self.notification_bot().email) self.assertEqual(msg.content, expected_msg.strip())
def test_notify_bot_owner_on_invalid_json(self) -> None: @api_key_only_webhook_view('ClientName', notify_bot_owner_on_invalid_json=False) def my_webhook_no_notify(request: HttpRequest, user_profile: UserProfile) -> None: raise InvalidJSONError("Malformed JSON") @api_key_only_webhook_view('ClientName', notify_bot_owner_on_invalid_json=True) def my_webhook_notify(request: HttpRequest, user_profile: UserProfile) -> None: raise InvalidJSONError("Malformed JSON") webhook_bot_email = '*****@*****.**' webhook_bot_realm = get_realm('zulip') webhook_bot = get_user(webhook_bot_email, webhook_bot_realm) webhook_bot_api_key = get_api_key(webhook_bot) request = HostRequestMock() request.POST['api_key'] = webhook_bot_api_key request.host = "zulip.testserver" expected_msg = INVALID_JSON_MESSAGE.format(webhook_name='ClientName') last_message_id = self.get_last_message().id with self.assertRaisesRegex(JsonableError, "Malformed JSON"): my_webhook_no_notify(request) # type: ignore # mypy doesn't seem to apply the decorator # First verify that without the setting, it doesn't send a PM to bot owner. msg = self.get_last_message() self.assertEqual(msg.id, last_message_id) self.assertNotEqual(msg.content, expected_msg.strip()) # Then verify that with the setting, it does send such a message. with self.assertRaisesRegex(JsonableError, "Malformed JSON"): my_webhook_notify(request) # type: ignore # mypy doesn't seem to apply the decorator msg = self.get_last_message() self.assertNotEqual(msg.id, last_message_id) self.assertEqual(msg.sender.email, self.notification_bot().email) self.assertEqual(msg.content, expected_msg.strip())
def test_notify_bot_owner_on_invalid_json(self) -> None: @webhook_view("ClientName", notify_bot_owner_on_invalid_json=False) def my_webhook_no_notify(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: raise InvalidJSONError("Malformed JSON") @webhook_view("ClientName", notify_bot_owner_on_invalid_json=True) def my_webhook_notify(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: raise InvalidJSONError("Malformed JSON") webhook_bot_email = "*****@*****.**" webhook_bot_realm = get_realm("zulip") webhook_bot = get_user(webhook_bot_email, webhook_bot_realm) webhook_bot_api_key = get_api_key(webhook_bot) request = HostRequestMock() request.POST["api_key"] = webhook_bot_api_key request.host = "zulip.testserver" expected_msg = INVALID_JSON_MESSAGE.format(webhook_name="ClientName") last_message_id = self.get_last_message().id with self.assertRaisesRegex(JsonableError, "Malformed JSON"): my_webhook_no_notify(request) # First verify that without the setting, it doesn't send a PM to bot owner. msg = self.get_last_message() self.assertEqual(msg.id, last_message_id) self.assertNotEqual(msg.content, expected_msg.strip()) # Then verify that with the setting, it does send such a message. with self.assertRaisesRegex(JsonableError, "Malformed JSON"): my_webhook_notify(request) msg = self.get_last_message() self.assertNotEqual(msg.id, last_message_id) self.assertEqual(msg.sender.id, self.notification_bot(webhook_bot_realm).id) self.assertEqual(msg.content, expected_msg.strip())
def test_api_key_only_webhook_view(self): # type: () -> None @api_key_only_webhook_view('ClientName') def my_webhook(request, user_profile): # type: (HttpRequest, UserProfile) -> Text return user_profile.email @api_key_only_webhook_view('ClientName') def my_webhook_raises_exception(request, user_profile): # type: (HttpRequest, UserProfile) -> None raise Exception("raised by webhook function") webhook_bot_email = '*****@*****.**' webhook_bot_realm = get_realm('zulip') webhook_bot = get_user(webhook_bot_email, webhook_bot_realm) webhook_bot_api_key = webhook_bot.api_key webhook_client_name = "ZulipClientNameWebhook" request = HostRequestMock() request.POST['api_key'] = 'not_existing_api_key' with self.assertRaisesRegex(JsonableError, "Invalid API key"): my_webhook(request) # Start a valid request here request.POST['api_key'] = webhook_bot_api_key with mock.patch('logging.warning') as mock_warning: with self.assertRaisesRegex(JsonableError, "Account is not associated with this subdomain"): api_result = my_webhook(request) mock_warning.assert_called_with( "User {} ({}) attempted to access API on wrong " "subdomain ({})".format(webhook_bot_email, 'zulip', '')) with mock.patch('logging.warning') as mock_warning: with self.assertRaisesRegex(JsonableError, "Account is not associated with this subdomain"): request.host = "acme." + settings.EXTERNAL_HOST api_result = my_webhook(request) mock_warning.assert_called_with( "User {} ({}) attempted to access API on wrong " "subdomain ({})".format(webhook_bot_email, 'zulip', 'acme')) request.host = "zulip.testserver" # Test when content_type is application/json and request.body # is valid JSON; exception raised in the webhook function # should be re-raised with mock.patch('zerver.decorator.webhook_logger.exception') as mock_exception: with self.assertRaisesRegex(Exception, "raised by webhook function"): request.body = "{}" request.content_type = 'application/json' my_webhook_raises_exception(request) # Test when content_type is not application/json; exception raised # in the webhook function should be re-raised with mock.patch('zerver.decorator.webhook_logger.exception') as mock_exception: with self.assertRaisesRegex(Exception, "raised by webhook function"): request.body = "notjson" request.content_type = 'text/plain' my_webhook_raises_exception(request) # Test when content_type is application/json but request.body # is not valid JSON; invalid JSON should be logged and the # exception raised in the webhook function should be re-raised with mock.patch('zerver.decorator.webhook_logger.exception') as mock_exception: with self.assertRaisesRegex(Exception, "raised by webhook function"): request.body = "invalidjson" request.content_type = 'application/json' my_webhook_raises_exception(request) message = """ user: {email} ({realm}) client: {client_name} URL: {path_info} content_type: {content_type} body: {body} """ mock_exception.assert_called_with(message.format( email=webhook_bot_email, realm=webhook_bot_realm.string_id, client_name=webhook_client_name, path_info=request.META.get('PATH_INFO'), content_type=request.content_type, body=request.body, )) with self.settings(RATE_LIMITING=True): with mock.patch('zerver.decorator.rate_limit_user') as rate_limit_mock: api_result = my_webhook(request) # Verify rate limiting was attempted. self.assertTrue(rate_limit_mock.called) # Verify decorator set the magic _email field used by some of our back end logging. self.assertEqual(request._email, webhook_bot_email) # Verify the main purpose of the decorator, which is that it passed in the # user_profile to my_webhook, allowing it return the correct # email for the bot (despite the API caller only knowing the API key). self.assertEqual(api_result, webhook_bot_email) # Now deactivate the user webhook_bot.is_active = False webhook_bot.save() with self.assertRaisesRegex(JsonableError, "Account not active"): my_webhook(request) # Reactive the user, but deactivate their realm. webhook_bot.is_active = True webhook_bot.save() webhook_bot.realm.deactivated = True webhook_bot.realm.save() with self.assertRaisesRegex(JsonableError, "Realm for account has been deactivated"): my_webhook(request)