Skip to content

Commit

Permalink
fix bypass via admin login
Browse files Browse the repository at this point in the history
inspired by django-two-factor-auth
  • Loading branch information
xi committed Apr 15, 2022
1 parent d8f7f3a commit 32f656e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 1 deletion.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ pip install django-mfa3
## Usage

1. Add `'mfa'` to `INSTALLED_APPS`
2. Use `mfa.views.LoginView` instead of the regular login view
2. Use `mfa.views.LoginView` instead of the regular login view. (Be sure to
remove any other login routes, otherwise the multi factor authentication
can be circumvented. The admin login will automatically be patched to
redirect to the regular login.)
3. Set `MFA_DOMAIN = 'example.com'` and `MFA_SITE_TITLE = 'My site'`
4. Register URLs: `path('mfa/', include('mfa.urls', namespace='mfa')`
5. The included templates are just examples, so you should [replace them](https://docs.djangoproject.com/en/stable/howto/overriding-templates/) with your own
Expand Down
22 changes: 22 additions & 0 deletions mfa/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
from django.contrib import admin
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.views import redirect_to_login
from django.urls import reverse

from .models import MFAKey

original_login = admin.AdminSite.login


def custom_login(self, request, extra_context=None):
next_url = (
request.GET.get(REDIRECT_FIELD_NAME)
or request.POST.get(REDIRECT_FIELD_NAME)
or reverse('admin:index')
)
return redirect_to_login(next_url)


def patch_admin():
setattr(admin.AdminSite, 'login', custom_login)


def unpatch_admin():
setattr(admin.AdminSite, 'login', original_login)


@admin.register(MFAKey)
class MFAKeyAdmin(admin.ModelAdmin):
Expand Down
10 changes: 10 additions & 0 deletions mfa/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.apps import AppConfig


class TwoFactorConfig(AppConfig):
name = 'mfa'
verbose_name = 'Multi Factor Authentication'

def ready(self):
from .admin import patch_admin
patch_admin()
25 changes: 25 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,31 @@ def test_public(self):
self.assertEqual(res.url, '/')


class PatchAdminTest(TestCase):
def test_root(self):
res = self.client.get('/admin/')
self.assertEqual(res.status_code, 302)
self.assertEqual(res.url, '/admin/login/?next=/admin/')

res = self.client.get(res.url)
self.assertEqual(res.status_code, 302)
self.assertEqual(res.url, '/login/?next=/admin/')

def test_app(self):
res = self.client.get('/admin/mfa/')
self.assertEqual(res.status_code, 302)
self.assertEqual(res.url, '/admin/login/?next=/admin/mfa/')

res = self.client.get(res.url)
self.assertEqual(res.status_code, 302)
self.assertEqual(res.url, '/login/?next=/admin/mfa/')

def test_login(self):
res = self.client.get('/admin/login/')
self.assertEqual(res.status_code, 302)
self.assertEqual(res.url, '/login/?next=/admin/')


class QRCodeTest(TestCase):
def test_is_svg(self):
code = get_qrcode('some_data')
Expand Down
2 changes: 2 additions & 0 deletions tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib import admin
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import LogoutView
from django.http import HttpResponse
Expand All @@ -16,5 +17,6 @@ def dummy(request):
path('', login_required(dummy)),
path('login/', LoginView.as_view()),
path('logout/', public(LogoutView.as_view(next_page='/'))),
path('admin/', admin.site.urls),
path('mfa/', include('mfa.urls', namespace='mfa')),
]

0 comments on commit 32f656e

Please sign in to comment.