1+ from functools import update_wrapper
2+
13from django .conf import settings
24from django .contrib import admin
35from django .contrib .admin import AdminSite
46from django .contrib .auth import REDIRECT_FIELD_NAME
57from django .contrib .auth .views import redirect_to_login
8+ from django .core .urlresolvers import reverse
69from django .shortcuts import resolve_url
710from django .utils .http import is_safe_url
11+ from django .utils .translation import ugettext
812
913from .models import PhoneDevice
1014from .utils import monkeypatch_method
15+ from .views import BackupTokensView , LoginView , ProfileView , SetupView
16+
17+
18+ class AdminLoginView (LoginView ):
19+ form_templates = {
20+ 'auth' : 'two_factor/admin/_wizard_form_auth.html' ,
21+ 'token' : 'two_factor/admin/_wizard_form_token.html' ,
22+ 'backup' : 'two_factor/admin/_wizard_form_backup.html' ,
23+ }
24+ redirect_url = 'admin:two_factor:setup'
25+ template_name = 'two_factor/admin/login.html'
26+
27+ def get_context_data (self , form , ** kwargs ):
28+ context = super (AdminLoginView , self ).get_context_data (form , ** kwargs )
29+ if self .kwargs ['extra_context' ]:
30+ context .update (self .kwargs ['extra_context' ])
31+ user_is_validated = getattr (self .request .user , 'is_verified' , None )
32+ context .update ({
33+ 'cancel_url' : reverse ('admin:index' if user_is_validated else 'admin:login' ),
34+ 'wizard_form_template' : self .form_templates .get (self .steps .current ),
35+ })
36+ return context
37+
38+ def get_redirect_url (self ):
39+ redirect_to = self .request .GET .get (self .redirect_field_name , '' )
40+ url_is_safe = is_safe_url (url = redirect_to , host = self .request .get_host ())
41+ if url_is_safe :
42+ self .request .session [REDIRECT_FIELD_NAME ] = redirect_to
43+ user_is_validated = getattr (self .request .user , 'is_verified' , None )
44+ if not url_is_safe or not user_is_validated :
45+ redirect_to = resolve_url (self .redirect_url )
46+ return redirect_to
47+
48+
49+ admin_login_view = AdminLoginView .as_view ()
50+
51+
52+ class AdminSetupView (SetupView ):
53+ form_templates = {
54+ 'method' : 'two_factor/admin/_wizard_form_method.html' ,
55+ 'generator' : 'two_factor/admin/_wizard_form_generator.html' ,
56+ 'sms' : 'two_factor/admin/_wizard_form_phone_number.html' ,
57+ 'call' : 'two_factor/admin/_wizard_form_phone_number.html' ,
58+ 'validation' : 'two_factor/admin/_wizard_form_validation.html' ,
59+ 'yubikey' : 'two_factor/admin/_wizard_form_yubikey.html' ,
60+ }
61+ redirect_url = 'admin:two_factor:profile'
62+ template_name = 'two_factor/admin/setup.html'
63+
64+ def get_context_data (self , form , ** kwargs ):
65+ context = super (AdminSetupView , self ).get_context_data (form , ** kwargs )
66+ user_is_validated = getattr (self .request .user , 'is_verified' , None )
67+ context .update ({
68+ 'cancel_url' : reverse ('admin:two_factor:profile' if user_is_validated else 'admin:login' ),
69+ 'site_header' : ugettext ("Enable Two-Factor Authentication" ),
70+ 'title' : ugettext ("Enable Two-Factor Authentication" ),
71+ 'wizard_form_template' : self .form_templates .get (self .steps .current ),
72+ })
73+ return context
74+
75+ def get_redirect_url (self ):
76+ redirect_to = self .request .session .pop (REDIRECT_FIELD_NAME , '' )
77+ url_is_safe = is_safe_url (url = redirect_to , host = self .request .get_host ())
78+ user_is_validated = self .request .user .is_verified ()
79+ if url_is_safe and user_is_validated :
80+ return redirect_to
81+ return super (AdminSetupView , self ).get_redirect_url ()
82+
83+ admin_setup_view = AdminSetupView .as_view ()
84+
85+
86+ class AdminBackupTokensView (BackupTokensView ):
87+ redirect_url = 'admin:two_factor:backup_tokens'
88+ template_name = 'two_factor/admin/backup_tokens.html'
89+
90+ def get_context_data (self , ** kwargs ):
91+ context = super (AdminBackupTokensView , self ).get_context_data (** kwargs )
92+ context .update ({
93+ 'site_header' : ugettext ("Backup Tokens" ),
94+ 'title' : ugettext ("Backup Tokens" ),
95+ })
96+ return context
97+
98+ admin_backup_tokens_view = AdminBackupTokensView .as_view ()
99+
100+
101+ class AdminProfileView (ProfileView ):
102+ template_name = 'two_factor/admin/profile.html'
103+
104+ def get_context_data (self , ** kwargs ):
105+ context = super (AdminProfileView , self ).get_context_data (** kwargs )
106+ context .update ({
107+ 'site_header' : ugettext ("Account Security" ),
108+ 'title' : ugettext ("Account Security" ),
109+ })
110+ return context
111+
112+ admin_profile_view = AdminProfileView .as_view ()
11113
12114
13115class AdminSiteOTPRequiredMixin (object ):
@@ -27,16 +129,38 @@ def has_permission(self, request):
27129 return False
28130 return request .user .is_verified ()
29131
132+ def get_urls (self ):
133+ from django .conf .urls import include , url
134+
135+ def wrap (view , cacheable = False ):
136+ def wrapper (* args , ** kwargs ):
137+ return self .admin_view (view , cacheable )(* args , ** kwargs )
138+ wrapper .admin_site = self
139+ return update_wrapper (wrapper , view )
140+
141+ urlpatterns_2fa = [
142+ url (r'^profile/$' , wrap (self .two_factor_profile ), name = 'profile' ),
143+ url (r'^setup/$' , self .two_factor_setup , name = 'setup' ),
144+ url (r'^backup/tokens/$' , wrap (self .two_factor_backup_tokens ), name = 'backup_tokens' ),
145+ ]
146+
147+ urlpatterns = [
148+ url (r'^two_factor/' , include (urlpatterns_2fa , namespace = 'two_factor' ))
149+ ]
150+ urlpatterns += super (AdminSiteOTPRequiredMixin , self ).get_urls ()
151+ return urlpatterns
152+
30153 def login (self , request , extra_context = None ):
31- """
32- Redirects to the site login page for the given HttpRequest.
33- """
34- redirect_to = request .POST .get (REDIRECT_FIELD_NAME , request .GET .get (REDIRECT_FIELD_NAME ))
154+ return admin_login_view (request , extra_context = extra_context )
35155
36- if not redirect_to or not is_safe_url ( url = redirect_to , host = request . get_host () ):
37- redirect_to = resolve_url ( settings . LOGIN_REDIRECT_URL )
156+ def two_factor_profile ( self , request ):
157+ return admin_profile_view ( request )
38158
39- return redirect_to_login (redirect_to )
159+ def two_factor_setup (self , request ):
160+ return admin_setup_view (request )
161+
162+ def two_factor_backup_tokens (self , request ):
163+ return admin_backup_tokens_view (request )
40164
41165
42166class AdminSiteOTPRequired (AdminSiteOTPRequiredMixin , AdminSite ):
0 commit comments