From b19754a45ff6e6b784fc698dcac19a7f588b66e9 Mon Sep 17 00:00:00 2001
From: Graham Herceg
diff --git a/corehq/apps/domain/templates/login_and_password/two_factor/core/login_form.html b/corehq/apps/domain/templates/login_and_password/two_factor/core/login_form.html
index 92beadb3c119..81967ba38d24 100644
--- a/corehq/apps/domain/templates/login_and_password/two_factor/core/login_form.html
+++ b/corehq/apps/domain/templates/login_and_password/two_factor/core/login_form.html
@@ -1,4 +1,4 @@
-{% load i18n two_factor %}
+{% load i18n phonenumber %}
{% blocktrans %}Please enter the phone number you wish to be called on. +
{% blocktrans trimmed %}Please enter the phone number you wish to be called on. This number will be validated in the next step. {% endblocktrans %}
{% elif wizard.steps.current == 'validation' %} - {% if device.method == 'call' %} -{% blocktrans %}We are calling your phone right now, please enter the - digits you hear.{% endblocktrans %}
- {% elif device.method == 'sms' %} -{% blocktrans %}We sent you a text message, please enter the tokens we - sent.{% endblocktrans %}
+ {% if challenge_succeeded %} + {% if device.method == 'call' %} +{% blocktrans trimmed %}We are calling your phone right now, please enter the + digits you hear.{% endblocktrans %}
+ {% elif device.method == 'sms' %} +{% blocktrans trimmed %}We sent you a text message, please enter the tokens we + sent.{% endblocktrans %}
+ {% endif %} + {% else %} +{% blocktrans trimmed %}We've + encountered an issue with the selected authentication method. Please + go back and verify that you entered your information correctly, try + again, or use a different authentication method instead. If the issue + persists, contact the site administrator.{% endblocktrans %}
{% endif %} {% elif wizard.steps.current == 'yubikey' %} -{% blocktrans %}To identify and verify your YubiKey, please insert a +
{% blocktrans trimmed %}To identify and verify your YubiKey, please insert a token in the field below. Your YubiKey will be linked to your account.{% endblocktrans %}
{% endif %} diff --git a/corehq/apps/settings/forms.py b/corehq/apps/settings/forms.py index 47883a864a70..5c86e3371321 100644 --- a/corehq/apps/settings/forms.py +++ b/corehq/apps/settings/forms.py @@ -17,7 +17,6 @@ MethodForm, TOTPDeviceForm, ) -from two_factor.plugins.phonenumber.utils import get_available_phone_methods from two_factor.plugins.phonenumber.forms import ( PhoneNumberForm, PhoneNumberMethodForm, @@ -142,16 +141,8 @@ def clean_token(self): class HQTwoFactorMethodForm(MethodForm): - def __init__(self, *, allow_phone_2fa, **kwargs): - super().__init__(**kwargs) - if not allow_phone_2fa: - # Block people from setting up the phone method as their default - phone_methods = [method for method, _ in get_available_phone_methods()] - self.fields['method'].choices = [ - (method, display_name) - for method, display_name in self.fields['method'].choices - if method not in phone_methods - ] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_class = 'form form-horizontal' self.helper.label_class = 'col-sm-3 col-md-4 col-lg-2' @@ -172,7 +163,7 @@ def __init__(self, *, allow_phone_2fa, **kwargs): _("Back"), css_class='btn-default', type='submit', - value='welcome_setup', + value='welcome', name="wizard_goto_step", ), ) @@ -182,8 +173,8 @@ def __init__(self, *, allow_phone_2fa, **kwargs): class HQTOTPDeviceForm(TOTPDeviceForm): token = forms.IntegerField(required=False, label=_("Token"), min_value=1, max_value=int('9' * totp_digits())) - def __init__(self, **kwargs): - super(HQTOTPDeviceForm, self).__init__(**kwargs) + def __init__(self, key, user, **kwargs): + super(HQTOTPDeviceForm, self).__init__(key, user, **kwargs) self.helper = FormHelper() self.helper.form_class = 'form form-horizontal' self.helper.label_class = 'col-sm-3 col-md-4 col-lg-2' diff --git a/corehq/apps/settings/method.py b/corehq/apps/settings/method.py new file mode 100644 index 000000000000..8ea82ee9515e --- /dev/null +++ b/corehq/apps/settings/method.py @@ -0,0 +1,28 @@ +from two_factor.plugins.phonenumber.method import PhoneCallMethod, SMSMethod +from two_factor.plugins.registry import GeneratorMethod + +from corehq.apps.settings.forms import HQPhoneNumberForm, HQTOTPDeviceForm + + +class HQGeneratorMethod(GeneratorMethod): + + form_path = 'corehq.apps.settings.forms.HQTOTPDeviceForm' + + def get_setup_forms(self, *args): + return {self.code: HQTOTPDeviceForm} + + +class HQPhoneCallMethod(PhoneCallMethod): + + form_path = 'corehq.apps.settings.forms.HQPhoneNumberForm' + + def get_setup_forms(self, *args): + return {self.code: HQPhoneNumberForm} + + +class HQSMSMethod(SMSMethod): + + form_path = 'corehq.apps.settings.forms.HQPhoneNumberForm' + + def get_setup_forms(self, *args): + return {self.code: HQPhoneNumberForm} diff --git a/corehq/apps/settings/views.py b/corehq/apps/settings/views.py index 38b90cf53870..742af46b7e16 100644 --- a/corehq/apps/settings/views.py +++ b/corehq/apps/settings/views.py @@ -34,6 +34,7 @@ PhoneDeleteView, PhoneSetupView ) +from two_factor.plugins.registry import registry from dimagi.utils.web import json_response @@ -57,14 +58,13 @@ not_found, ) from corehq.apps.settings.exceptions import DuplicateApiKeyName +from corehq.apps.settings.method import HQGeneratorMethod, HQPhoneCallMethod, HQSMSMethod from corehq.apps.settings.forms import ( HQApiKeyForm, HQDeviceValidationForm, HQEmptyForm, HQPasswordChangeForm, - HQPhoneNumberForm, HQPhoneNumberMethodForm, - HQTOTPDeviceForm, HQTwoFactorMethodForm, ) from corehq.apps.sso.models import IdentityProvider @@ -409,26 +409,21 @@ class TwoFactorSetupView(BaseMyAccountView, SetupView): page_title = gettext_lazy("Two Factor Authentication Setup") form_list = ( - ('welcome_setup', HQEmptyForm), + ('welcome', HQEmptyForm), ('method', HQTwoFactorMethodForm), - ('generator', HQTOTPDeviceForm), - ('sms', HQPhoneNumberForm), - ('call', HQPhoneNumberForm), - ('validation', HQDeviceValidationForm), + # other forms are dynamically added ) @method_decorator(active_domains_required) @method_decorator(login_required) def dispatch(self, request, *args, **kwargs): - # this is only here to add the login_required decorator - return super(TwoFactorSetupView, self).dispatch(request, *args, **kwargs) + registry.unregister('generator') + registry.register(HQGeneratorMethod()) + if _user_can_use_phone(request.user): + registry.register(HQSMSMethod()) + registry.register(HQPhoneCallMethod()) - def get_form_kwargs(self, step=None): - kwargs = super().get_form_kwargs(step) - if step == 'method': - kwargs.setdefault('allow_phone_2fa', _user_can_use_phone(self.request.couch_user)) - - return kwargs + return super(TwoFactorSetupView, self).dispatch(request, *args, **kwargs) class TwoFactorSetupCompleteView(BaseMyAccountView, SetupCompleteView): @@ -544,17 +539,11 @@ def dispatch(self, request, *args, **kwargs): class TwoFactorResetView(TwoFactorSetupView): urlname = 'reset' - form_list = ( - ('welcome_reset', HQEmptyForm), - ('method', HQTwoFactorMethodForm), - ('generator', HQTOTPDeviceForm), - ('sms', HQPhoneNumberForm), - ('call', HQPhoneNumberForm), - ('validation', HQDeviceValidationForm), - ) - def get(self, request, *args, **kwargs): default_device(request.user).delete() + # django-two-factor-auth caches the default device on the user so clear that too + from two_factor.utils import USER_DEFAULT_DEVICE_ATTR_NAME + delattr(request.user, USER_DEFAULT_DEVICE_ATTR_NAME) return super(TwoFactorResetView, self).get(request, *args, **kwargs) From 6d10fa8a94014c7cf9871ed25c9c44d10ec136b0 Mon Sep 17 00:00:00 2001 From: Graham Herceg{% blocktrans %}Your backup phone number will be used if your primary method of + {% if wizard.steps.current == 'setup' %} +
{% blocktrans trimmed %}Your backup phone number will be used if your primary method of registration is not available. Please enter a valid phone number.{% endblocktrans %}
{% elif wizard.steps.current == 'validation' %} -{% blocktrans %}We've sent a token to your phone number. Please +
{% blocktrans trimmed %}We've sent a token to your phone number. Please enter the token you've received.{% endblocktrans %}
{% endif %} diff --git a/corehq/apps/hqwebapp/urls.py b/corehq/apps/hqwebapp/urls.py index 0c08e098b96d..98153e0f2662 100644 --- a/corehq/apps/hqwebapp/urls.py +++ b/corehq/apps/hqwebapp/urls.py @@ -76,9 +76,9 @@ url(r'^account/two_factor/backup/tokens/$', TwoFactorBackupTokensView.as_view(), name=TwoFactorBackupTokensView.urlname), url(r'^account/two_factor/disable/$', TwoFactorDisableView.as_view(), name=TwoFactorDisableView.urlname), - url(r'^account/two_factor/backup/phone/register/$', TwoFactorPhoneSetupView.as_view(), + url(r'^account/two_factor/phone/register/$', TwoFactorPhoneSetupView.as_view(), name=TwoFactorPhoneSetupView.urlname), - url(r'^account/two_factor/backup/phone/unregister/(?P