Skip to content

Commit

Permalink
Decorate the login view with login_not_required on Django 5.1+
Browse files Browse the repository at this point in the history
  • Loading branch information
claudep committed Aug 10, 2024
1 parent 856d8a1 commit b64bbc6
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

### Added
- Support confirmation for Django 5.1.
- The login view is also decorated with the `login_not_required` decorator for
projects using the new `LoginRequiredMiddleware` available with Django 5.1+.

### Removed
- Dropped support for Django <4.2.
Expand Down
21 changes: 18 additions & 3 deletions tests/test_views_login.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import json
from importlib import import_module
from time import sleep
from unittest import mock
from unittest import mock, skipUnless

from django.conf import settings
from django.core.signing import BadSignature
from django.shortcuts import resolve_url
from django.test import RequestFactory, TestCase
from django.test.utils import override_settings
from django.test.utils import modify_settings, override_settings
from django.urls import reverse
from django_otp import DEVICE_ID_SESSION_KEY
from django_otp.oath import totp
Expand All @@ -18,12 +18,27 @@

from .utils import UserMixin, totp_str

try:
from django.contrib.auth.middleware import LoginRequiredMiddleware # NOQA
has_login_required_middleware = True
except ImportError:
# Django < 5.1
has_login_required_middleware = False


class LoginTest(UserMixin, TestCase):
def _post(self, data=None):
return self.client.post(reverse('two_factor:login'), data=data)

def test_form(self):
def test_get_to_login(self):
response = self.client.get(reverse('two_factor:login'))
self.assertContains(response, 'Password:')

@skipUnless(has_login_required_middleware, 'LoginRequiredMiddleware needs Django 5.1+')
@modify_settings(
MIDDLEWARE={'append': 'django.contrib.auth.middleware.LoginRequiredMiddleware'}
)
def test_get_to_login_with_loginrequiredmiddleware(self):
response = self.client.get(reverse('two_factor:login'))
self.assertContains(response, 'Password:')

Expand Down
17 changes: 16 additions & 1 deletion two_factor/views/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,27 @@
validate_remember_device_cookie,
)

try:
from django.contrib.auth.decorators import login_not_required
except ImportError:
# For Django < 5.1, copy the current Django implementation
def login_not_required(view_func):
"""
Decorator for views that allows access to unauthenticated requests.
"""
view_func.login_required = False
return view_func


logger = logging.getLogger(__name__)

REMEMBER_COOKIE_PREFIX = getattr(settings, 'TWO_FACTOR_REMEMBER_COOKIE_PREFIX', 'remember-cookie_')


@method_decorator([sensitive_post_parameters(), csrf_protect, never_cache], name='dispatch')
@method_decorator(
[login_not_required, sensitive_post_parameters(), csrf_protect, never_cache],
name='dispatch'
)
class LoginView(RedirectURLMixin, IdempotentSessionWizardView):
"""
View for handling the login process, including OTP verification.
Expand Down

0 comments on commit b64bbc6

Please sign in to comment.