1+ from binascii import unhexlify
2+
13from django .conf import settings
24from django .shortcuts import resolve_url
35from django .test import TestCase
46from django .test .utils import override_settings
7+ from django .urls import reverse
8+ from django_otp .oath import totp
59
610from two_factor .admin import patch_admin , unpatch_admin
711
8- from .utils import UserMixin
12+ from .utils import UserMixin , method_registry
913
1014
1115@override_settings (ROOT_URLCONF = 'tests.urls_admin' )
@@ -48,15 +52,16 @@ class OTPAdminSiteTest(UserMixin, TestCase):
4852 def setUp (self ):
4953 super ().setUp ()
5054 self .user = self .create_superuser ()
51- self .login_user ()
5255
5356 def test_otp_admin_without_otp (self ):
57+ self .login_user ()
5458 response = self .client .get ('/otp_admin/' , follow = True )
5559 redirect_to = '%s?next=/otp_admin/' % resolve_url (settings .LOGIN_URL )
5660 self .assertRedirects (response , redirect_to )
5761
5862 @override_settings (LOGIN_URL = 'two_factor:login' )
5963 def test_otp_admin_without_otp_named_url (self ):
64+ self .login_user ()
6065 response = self .client .get ('/otp_admin/' , follow = True )
6166 redirect_to = '%s?next=/otp_admin/' % resolve_url (settings .LOGIN_URL )
6267 self .assertRedirects (response , redirect_to )
@@ -66,3 +71,41 @@ def test_otp_admin_with_otp(self):
6671 self .login_user ()
6772 response = self .client .get ('/otp_admin/' )
6873 self .assertEqual (response .status_code , 200 )
74+
75+ @method_registry (['generator' ])
76+ def test_otp_admin_login_redirect_without_otp (self ):
77+ # Open URL that is protected
78+ # assert go to login page
79+ # assert the next param not in the session (but still in url)
80+ response = self .client .get ('/otp_admin/' , follow = True )
81+ redirect_to = '%s?next=/otp_admin/' % resolve_url (settings .LOGIN_URL )
82+ self .assertRedirects (response , redirect_to )
83+ self .assertEqual (self .client .session .get ('next' ), None )
84+
85+ # Log in given the last redirect
86+ # assert redirect to setup
87+ response = self .client .post (
88+ redirect_to ,
89+ {'auth-username' : 'bouke@example.com' , 'auth-password' : 'secret' , 'login_view-current_step' : 'auth' },
90+ )
91+ self .assertRedirects (response , reverse ('two_factor:setup' ))
92+ self .assertEqual (self .client .session .get ('next' ), '/otp_admin/' )
93+
94+ # Setup the device accordingly
95+ # assert redirect to setup completed
96+ # assert button for redirection to the original page
97+ response = self .client .post (reverse ('two_factor:setup' ), data = {'setup_view-current_step' : 'welcome' })
98+ self .assertContains (response , 'Token:' )
99+ self .assertContains (response , 'autofocus="autofocus"' )
100+ self .assertContains (response , 'inputmode="numeric"' )
101+ self .assertContains (response , 'autocomplete="one-time-code"' )
102+
103+ key = response .context_data ['keys' ].get ('generator' )
104+ bin_key = unhexlify (key .encode ())
105+ response = self .client .post (
106+ reverse ('two_factor:setup' ),
107+ data = {'setup_view-current_step' : 'generator' , 'generator-token' : totp (bin_key )},
108+ follow = True ,
109+ )
110+
111+ self .assertRedirects (response , '/otp_admin/' )
0 commit comments