diff --git a/tests/test_gateways.py b/tests/test_gateways.py index 1a43fe08f..7f9a15306 100644 --- a/tests/test_gateways.py +++ b/tests/test_gateways.py @@ -1,6 +1,7 @@ from unittest.mock import Mock, patch from urllib.parse import urlencode +from django.contrib.messages import get_messages from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse @@ -9,6 +10,7 @@ from two_factor.gateways.fake import Fake from two_factor.gateways.twilio.gateway import Twilio +from two_factor.middleware.threadlocals import get_current_request class TwilioGatewayTest(TestCase): @@ -76,6 +78,39 @@ def test_gateway(self, client): from_='+456', to='+123', method='GET', timeout=15, url='http://testserver/twilio/inbound/two_factor/%s/?locale=en-gb' % code) + @override_settings( + TWILIO_ACCOUNT_SID='SID', + TWILIO_AUTH_TOKEN='TOKEN', + TWILIO_CALLER_ID='+456', + TWILIO_ERROR_MESSAGE='Error sending SMS' + ) + @patch('two_factor.gateways.twilio.gateway.Client') + def test_gateway_error_handled(self, client): + twilio = Twilio() + client.assert_called_with('SID', 'TOKEN') + + client.return_value.messages.create.side_effect = Mock(side_effect=Exception('Test')) + code = '123456' + twilio.send_sms(device=Mock(number=PhoneNumber.from_string('+123')), token=code) + request = get_current_request() + storage = get_messages(request) + assert 'Error sending SMS' in [str(message) for message in storage] + + @override_settings( + TWILIO_ACCOUNT_SID='SID', + TWILIO_AUTH_TOKEN='TOKEN', + TWILIO_CALLER_ID='+456', + ) + @patch('two_factor.gateways.twilio.gateway.Client') + def test_gateway_error_not_handled(self, client): + twilio = Twilio() + client.assert_called_with('SID', 'TOKEN') + + client.return_value.messages.create.side_effect = Mock(side_effect=Exception('Test')) + with self.assertRaises(Exception): + code = '123456' + twilio.send_sms(device=Mock(number=PhoneNumber.from_string('+123')), token=code) + @override_settings( TWILIO_ACCOUNT_SID='SID', TWILIO_AUTH_TOKEN='TOKEN', diff --git a/two_factor/gateways/twilio/gateway.py b/two_factor/gateways/twilio/gateway.py index 8b1bde62b..4a2b33904 100644 --- a/two_factor/gateways/twilio/gateway.py +++ b/two_factor/gateways/twilio/gateway.py @@ -1,6 +1,7 @@ from urllib.parse import urlencode from django.conf import settings +from django.contrib import messages from django.urls import reverse from django.utils import translation from django.utils.translation import gettext, pgettext @@ -33,6 +34,12 @@ class Twilio(object): Should be set to a verified phone number. Twilio_ differentiates between numbers verified for making phone calls and sending text messages. + Optionally you may set an error message to display in case of error + sending a SMS. + + ``TWILIO_ERROR_MESSAGE`` + Should be set to a string with a custom text. + .. _Twilio: http://www.twilio.com/ """ def __init__(self): @@ -52,11 +59,19 @@ def make_call(self, device, token): url=uri, method='GET', timeout=15) def send_sms(self, device, token): - body = gettext('Your authentication token is %s') % token - self.client.messages.create( - to=device.number.as_e164, - from_=getattr(settings, 'TWILIO_CALLER_ID'), - body=body) + body = ugettext('Your authentication token is %s') % token + try: + self.client.messages.create( + to=device.number.as_e164, + from_=getattr(settings, 'TWILIO_CALLER_ID'), + body=body) + except Exception: + twilio_error_message = getattr(settings, 'TWILIO_ERROR_MESSAGE', None) + if twilio_error_message: + request = get_current_request() + messages.add_message(request, messages.ERROR, twilio_error_message) + else: + raise def validate_voice_locale(locale):