diff --git a/README.rst b/README.rst index 571e669..e4926eb 100644 --- a/README.rst +++ b/README.rst @@ -83,6 +83,7 @@ Configuration "VERIFY_SECURITY_CODE_ONLY_ONCE": False, # If False, then a security code can be used multiple times for verification } +- In case of using Kavenegar as your backend service, you have to replace ``BACKEND`` with ``phone_verify.backends.kavenegar.KavenegarBackend`` and locate your ``API-KEY` in ``SECRET`` and ``SENDER`` in ``FROM``, extra fields could be omitted. Usage ----- diff --git a/phone_verify/backends/__init__.py b/phone_verify/backends/__init__.py index 3d257c7..fbbd5ab 100644 --- a/phone_verify/backends/__init__.py +++ b/phone_verify/backends/__init__.py @@ -13,8 +13,8 @@ def get_sms_backend(phone_number): if not backend: backend_import = DEFAULT_SERVICE - if settings.PHONE_VERIFICATION['DEFAULT'].get("BACKEND", None): - backend_import = settings.PHONE_VERIFICATION['DEFAULT']["BACKEND"] + if settings.PHONE_VERIFICATION.get("BACKEND", None): + backend_import = settings.PHONE_VERIFICATION["BACKEND"] backend_cls = import_string(backend_import) - return backend_cls(**settings.PHONE_VERIFICATION['DEFAULT']["OPTIONS"]) + return backend_cls(**settings.PHONE_VERIFICATION["OPTIONS"]) diff --git a/phone_verify/backends/base.py b/phone_verify/backends/base.py index aa7ba20..59fbd22 100644 --- a/phone_verify/backends/base.py +++ b/phone_verify/backends/base.py @@ -36,7 +36,7 @@ def generate_security_code(cls): """ Returns a unique random `security_code` for given `TOKEN_LENGTH` in the settings. """ - token_length = django_settings.PHONE_VERIFICATION['DEFAULT'].get( + token_length = django_settings.PHONE_VERIFICATION.get( "TOKEN_LENGTH", DEFAULT_TOKEN_LENGTH ) return get_random_string(token_length, allowed_chars="0123456789") @@ -56,7 +56,7 @@ def check_security_code_expiry(cls, stored_verification): Returns True if the `security_code` for the `stored_verification` is expired. """ time_difference = timezone.now() - stored_verification.created_at - if time_difference.seconds > django_settings.PHONE_VERIFICATION['DEFAULT'].get( + if time_difference.seconds > django_settings.PHONE_VERIFICATION.get( "SECURITY_CODE_EXPIRATION_TIME" ): return True @@ -126,7 +126,7 @@ def validate_security_code(self, security_code, phone_number, session_token): return stored_verification, self.SECURITY_CODE_EXPIRED # check security_code is not verified - if stored_verification.is_verified and django_settings.PHONE_VERIFICATION['DEFAULT'].get( + if stored_verification.is_verified and django_settings.PHONE_VERIFICATION.get( "VERIFY_SECURITY_CODE_ONLY_ONCE" ): return stored_verification, self.SECURITY_CODE_VERIFIED diff --git a/phone_verify/backends/kavenegar.py b/phone_verify/backends/kavenegar.py index a02c516..7380ebc 100644 --- a/phone_verify/backends/kavenegar.py +++ b/phone_verify/backends/kavenegar.py @@ -8,41 +8,25 @@ from .base import BaseBackend +class KavenegarException(APIException, HTTPException): + pass + + class KavenegarBackend(BaseBackend): def __init__(self, **options): super(KavenegarBackend, self).__init__(**options) # Lower case it just to be sure options = {key.lower(): value for key, value in options.items()} - self._api_key = options.get("api_key", None) - self._sender = options.get("sender", None) + self.api_key = options.get("secret", None) + self.sender = options.get("from", None) - self._api = KavenegarAPI(self._api_key) + self.client = KavenegarAPI(self.api_key) + self.exception_class = KavenegarException def send_sms(self, number, message): - try: - params = { - 'receptor': number, - 'template': '', - 'token': message, - 'type': 'sms' - } - response = self._api.sms_send(params) - print(response) - except APIException as exp: - print(exp) - except HTTPException as exp: - print(exp) + params = {'receptor': number, 'template': '', 'token': message, 'type': 'sms'} + self.client.sms_send(params) def send_bulk_sms(self, numbers, message): - try: - params = { - 'sender': self._sender, - 'receptor': numbers, - 'message': message, - } - response = self._api.sms_sendarray(params) - print(response) - except APIException as exp: - print(exp) - except HTTPException as exp: - print(exp) + params = {'sender': self.sender, 'receptor': numbers, 'message': message, } + self.client.sms_sendarray(params) diff --git a/phone_verify/services.py b/phone_verify/services.py index f95f5c8..5324150 100644 --- a/phone_verify/services.py +++ b/phone_verify/services.py @@ -21,7 +21,7 @@ class PhoneVerificationService(object): try: - phone_settings = settings.PHONE_VERIFICATION['DEFAULT'] + phone_settings = settings.PHONE_VERIFICATION except AttributeError: raise ImproperlyConfigured("Please define PHONE_VERIFICATION in settings") @@ -44,7 +44,7 @@ def send_verification(self, number, security_code): def _generate_message(self, security_code): return self.verification_message.format( - app=settings.PHONE_VERIFICATION['DEFAULT'].get("APP_NAME", DEFAULT_APP_NAME), + app=settings.PHONE_VERIFICATION.get("APP_NAME", DEFAULT_APP_NAME), security_code=security_code, ) @@ -58,7 +58,7 @@ def _check_required_settings(self): "SECURITY_CODE_EXPIRATION_TIME", "VERIFY_SECURITY_CODE_ONLY_ONCE", } - user_settings = set(settings.PHONE_VERIFICATION['DEFAULT'].keys()) + user_settings = set(settings.PHONE_VERIFICATION.keys()) if not required_settings.issubset(user_settings): raise ImproperlyConfigured( "Please specify following settings in settings.py: {}".format( diff --git a/tests/test_api.py b/tests/test_api.py index cc020f0..2f1eabd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -23,14 +23,27 @@ def test_phone_registration_sends_message(client, mocker): url = reverse("phone-register") phone_number = PHONE_NUMBER data = {"phone_number": phone_number} - twilio_api = mocker.patch( + api = mocker.patch( "phone_verify.services.PhoneVerificationService.send_verification" ) response = client.post(url, data) assert response.status_code == 200 - assert twilio_api.called + assert api.called + assert "session_token" in response.data + sms_verification = apps.get_model("phone_verify", "SMSVerification") + assert sms_verification.objects.get( + session_token=response.data["session_token"], phone_number=phone_number + ) + + settings.DJANGO_SETTINGS["PHONE_VERIFICATION"][ + "BACKEND" + ] = 'phone_verify.backends.kavenegar.KavenegarBackend' + response = client.post(url, data) + + assert response.status_code == 200 + assert api.called assert "session_token" in response.data sms_verification = apps.get_model("phone_verify", "SMSVerification") assert sms_verification.objects.get( @@ -151,7 +164,7 @@ def test_verified_security_code(client): } # Security code verification is restricted to one time - settings.DJANGO_SETTINGS["PHONE_VERIFICATION"]['DEFAULT'][ + settings.DJANGO_SETTINGS["PHONE_VERIFICATION"][ "VERIFY_SECURITY_CODE_ONLY_ONCE" ] = True response = client.json.post(url, data=data) @@ -160,7 +173,7 @@ def test_verified_security_code(client): assert response_data["non_field_errors"][0] == "Security code is already verified" # Security code verification is not restricted to one time - settings.DJANGO_SETTINGS["PHONE_VERIFICATION"]['DEFAULT'][ + settings.DJANGO_SETTINGS["PHONE_VERIFICATION"][ "VERIFY_SECURITY_CODE_ONLY_ONCE" ] = False response = client.json.post(url, data=data) diff --git a/tests/test_services.py b/tests/test_services.py index 5a3c55f..6979535 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -12,7 +12,7 @@ def test_message_generation_and_sending_service(client, mocker): service = PhoneVerificationService(phone_number="+13478379634") - service_api = mocker.patch(f'{settings.PHONE_VERIFICATION["DEFAULT"]["BACKEND"]}.send_sms') + service_api = mocker.patch(f'{settings.PHONE_VERIFICATION["BACKEND"]}.send_sms') service.send_verification("+13478379634", "123456") assert service_api.called diff --git a/tests/test_settings.py b/tests/test_settings.py index faa8a2e..06356a6 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -9,31 +9,18 @@ ], # PHONE VERIFICATION "PHONE_VERIFICATION": { - "TWILIO": { - "BACKEND": "phone_verify.backends.twilio.TwilioBackend", - "OPTIONS": { - "SID": "fake", - "SECRET": "fake", - "FROM": "+14755292729", - "SANDBOX_TOKEN": "123456", - }, - "TOKEN_LENGTH": 6, - "MESSAGE": "Welcome to {app}! Please use security code {security_code} to proceed.", - "APP_NAME": "Phone Verify", - "SECURITY_CODE_EXPIRATION_TIME": 1, # In seconds only - "VERIFY_SECURITY_CODE_ONLY_ONCE": False, + "BACKEND": "phone_verify.backends.twilio.TwilioBackend", + "OPTIONS": { + "SID": "fake", + "SECRET": "fake", + "FROM": "+14755292729", + "SANDBOX_TOKEN": "123456", }, - "DEFAULT": { - "BACKEND": "phone_verify.backends.kavenegar.KavenegarBackend", - "OPTIONS": { - "API_KEY": "fake", - "SENDER": "+14755292729" - }, - "TOKEN_LENGTH": 6, - "MESSAGE": "Welcome to {app}! Please use security code {security_code} to proceed.", - "APP_NAME": "Phone Verify", - "SECURITY_CODE_EXPIRATION_TIME": 1, # In seconds only - "VERIFY_SECURITY_CODE_ONLY_ONCE": False, - } + "TOKEN_LENGTH": 6, + "MESSAGE": "Welcome to {app}! Please use security code {security_code} to proceed.", + "APP_NAME": "Phone Verify", + "SECURITY_CODE_EXPIRATION_TIME": 1, # In seconds only + "VERIFY_SECURITY_CODE_ONLY_ONCE": False, + }, }