def zulip_otp_required(view: Any=None, redirect_field_name: str='next', login_url: str=settings.HOME_NOT_LOGGED_IN, ) -> Callable[..., HttpResponse]: """ The reason we need to create this function is that the stock otp_required decorator doesn't play well with tests. We cannot enable/disable if_configured parameter during tests since the decorator retains its value due to closure. Similar to :func:`~django.contrib.auth.decorators.login_required`, but requires the user to be :term:`verified`. By default, this redirects users to :setting:`OTP_LOGIN_URL`. """ def test(user: UserProfile) -> bool: """ :if_configured: If ``True``, an authenticated user with no confirmed OTP devices will be allowed. Default is ``False``. If ``False``, 2FA will not do any authentication. """ if_configured = settings.TWO_FACTOR_AUTHENTICATION_ENABLED if not if_configured: return True return user.is_verified() or (_user_is_authenticated(user) and not user_has_device(user)) decorator = django_user_passes_test(test, login_url=login_url, redirect_field_name=redirect_field_name) return decorator if (view is None) else decorator(view)
def zulip_otp_required( redirect_field_name: str = "next", login_url: str = settings.HOME_NOT_LOGGED_IN, ) -> Callable[[ViewFuncT], ViewFuncT]: """ The reason we need to create this function is that the stock otp_required decorator doesn't play well with tests. We cannot enable/disable if_configured parameter during tests since the decorator retains its value due to closure. Similar to :func:`~django.contrib.auth.decorators.login_required`, but requires the user to be :term:`verified`. By default, this redirects users to :setting:`OTP_LOGIN_URL`. """ def test(user: UserProfile) -> bool: """ :if_configured: If ``True``, an authenticated user with no confirmed OTP devices will be allowed. Also, non-authenticated users will be allowed as spectator users. Default is ``False``. If ``False``, 2FA will not do any authentication. """ if_configured = settings.TWO_FACTOR_AUTHENTICATION_ENABLED if not if_configured: return True # User has completed 2FA verification if user.is_verified(): return True # This request is unauthenticated (logged-out) access; 2FA is # not required or possible. # # TODO: Add a test for 2FA-enabled with web-public views. if not user.is_authenticated: # nocoverage return True # If the user doesn't have 2FA set up, we can't enforce 2FA. if not user_has_device(user): return True # User has configured 2FA and is not verified, so the user # fails the test (and we should redirect to the 2FA view). return False decorator = django_user_passes_test( test, login_url=login_url, redirect_field_name=redirect_field_name ) return decorator