jazzband / django-two-factor-auth

Complete Two-Factor Authentication for Django providing the easiest integration into most Django projects.
MIT License
1.71k stars 448 forks source link

Setup wizard crashes if registry is empty #742

Open dawidwolski-identt opened 1 month ago

dawidwolski-identt commented 1 month ago

Setup wizard crashes on MethodForm if registry is empty.

Expected Behavior

The wizard should show warning and do not allow user to proceed if there is no methods in the registry.

Current Behavior

HTTP 500 and

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/dist-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.11/dist-packages/sentry_sdk/integrations/django/views.py", line 90, in sentry_wrapped_callback
    return callback(request, *args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/formtools/wizard/views.py", line 244, in dispatch
    response = super().dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/two_factor/views/utils.py", line 169, in post
    return super().post(*args, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/formtools/wizard/views.py", line 305, in post
    return self.render_next_step(form)
  File "/usr/local/lib/python3.11/dist-packages/two_factor/views/core.py", line 529, in render_next_step
    return super().render_next_step(form, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/formtools/wizard/views.py", line 316, in render_next_step
    new_form = self.get_form(
  File "/usr/local/lib/python3.11/dist-packages/two_factor/views/core.py", line 491, in get_form
    return super().get_form(step=step, **kwargs)
  File "/usr/local/lib/python3.11/dist-packages/formtools/wizard/views.py", line 426, in get_form
    return form_class(**kwargs)
  File "/usr/local/lib/python3.11/dist-packages/two_factor/forms.py", line 26, in __init__
    method.initial = method.choices[0][0]
IndexError: list index out of range

Possible Solution

Do not allow user to proceed to the method step (gray out Next button on "welcome" step) or show message instead of method list on "method" step.

Steps to Reproduce (for bugs)

  1. Remove all methods from registry
  2. Go to setup wizard
  3. Proceed to "method" step

Context

We allow administrators to select which methods are allowed. They can disable all methods. We can handle that case on our side or we can discuss and choose solution here.

Your Environment

moggers87 commented 1 month ago

I feel like this is expected behaviour if all registry methods have been removed.

Maybe consider using a custom decorator, e.g.

def redirect_on_empty_registry(func):
  @wraps(func)
    def inner(request, *args, **kwargs):
      if len(registry.get_methods()) == 0:
        return redirect("somewhere-else")
      return func(request, *args, **kwargs)
  return inner

setup_view = redirect_on_empty_registry(SetupView.as_view())