Skip to content

Commit 714e6cb

Browse files
committed
Reworked admin auto-patching
1 parent cb86849 commit 714e6cb

File tree

3 files changed

+26
-26
lines changed

3 files changed

+26
-26
lines changed

docs/configuration.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ General Settings
1111
The admin currently does not enforce one-time passwords being set for
1212
admin users.
1313

14+
``TWO_FACTOR_FORCE_OTP_ADMIN`` (default: ``False``)
15+
Whether the Django admin will enforce 2 factor authentication.
16+
1417
``TWO_FACTOR_CALL_GATEWAY`` (default: ``None``)
1518
Which gateway to use for making phone calls. Should be set to a module or
1619
object providing a ``make_call`` method. Currently two gateways are bundled:

two_factor/admin.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44
from django.contrib import admin
55
from django.contrib.admin import AdminSite
66
from django.contrib.auth import REDIRECT_FIELD_NAME
7-
from django.contrib.auth.views import redirect_to_login
87
from django.core.urlresolvers import reverse
98
from django.shortcuts import resolve_url
109
from django.utils.http import is_safe_url
1110
from django.utils.translation import ugettext
1211

1312
from .models import PhoneDevice
14-
from .utils import monkeypatch_method
1513
from .views import BackupTokensView, LoginView, ProfileView, SetupView
1614

1715

@@ -129,6 +127,9 @@ def has_permission(self, request):
129127
return False
130128
return request.user.is_verified()
131129

130+
131+
class AdminSiteOTPMixin(object):
132+
132133
def get_urls(self):
133134
from django.conf.urls import include, url
134135

@@ -147,7 +148,7 @@ def wrapper(*args, **kwargs):
147148
urlpatterns = [
148149
url(r'^two_factor/', include(urlpatterns_2fa, namespace='two_factor'))
149150
]
150-
urlpatterns += super(AdminSiteOTPRequiredMixin, self).get_urls()
151+
urlpatterns += super(AdminSiteOTPMixin, self).get_urls()
151152
return urlpatterns
152153

153154
def login(self, request, extra_context=None):
@@ -163,32 +164,36 @@ def two_factor_backup_tokens(self, request):
163164
return admin_backup_tokens_view(request)
164165

165166

166-
class AdminSiteOTPRequired(AdminSiteOTPRequiredMixin, AdminSite):
167+
class AdminSiteOTP(AdminSiteOTPMixin, AdminSite):
167168
"""
168-
AdminSite enforcing OTP verified staff users.
169+
AdminSite using OTP login.
169170
"""
170171
pass
171172

172173

173-
def patch_admin():
174-
@monkeypatch_method(AdminSite)
175-
def login(self, request, extra_context=None):
176-
"""
177-
Redirects to the site login page for the given HttpRequest.
178-
"""
179-
redirect_to = request.POST.get(REDIRECT_FIELD_NAME, request.GET.get(REDIRECT_FIELD_NAME))
174+
class AdminSiteOTPRequired(AdminSiteOTPMixin, AdminSiteOTPRequiredMixin, AdminSite):
175+
"""
176+
AdminSite enforcing OTP verified staff users.
177+
"""
178+
pass
180179

181-
if not redirect_to or not is_safe_url(url=redirect_to, host=request.get_host()):
182-
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
183180

184-
return redirect_to_login(redirect_to)
181+
__default_admin_site__ = None
185182

186183

187-
def unpatch_admin():
188-
setattr(AdminSite, 'login', original_login)
184+
def patch_admin():
185+
global __default_admin_site__
186+
__default_admin_site__ = admin.site.__class__
187+
if getattr(settings, 'TWO_FACTOR_FORCE_OTP_ADMIN', False):
188+
admin.site.__class__ = AdminSiteOTPRequired
189+
else:
190+
admin.site.__class__ = AdminSiteOTP
189191

190192

191-
original_login = AdminSite.login
193+
def unpatch_admin():
194+
global __default_admin_site__
195+
admin.site.__class__ = __default_admin_site__
196+
__default_admin_site__ = None
192197

193198

194199
class PhoneDeviceAdmin(admin.ModelAdmin):

two_factor/utils.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,6 @@ def get_otpauth_url(accountname, secret, issuer=None, digits=None):
4747
return 'otpauth://totp/%s?%s' % (label, urlencode(query))
4848

4949

50-
# from http://mail.python.org/pipermail/python-dev/2008-January/076194.html
51-
def monkeypatch_method(cls):
52-
def decorator(func):
53-
setattr(cls, func.__name__, func)
54-
return func
55-
return decorator
56-
57-
5850
def totp_digits():
5951
"""
6052
Returns the number of digits (as configured by the TWO_FACTOR_TOTP_DIGITS setting)

0 commit comments

Comments
 (0)