enforce mfa on all frontend pages

This commit is contained in:
Matthias 2021-10-28 11:41:45 +02:00
parent 527bc4381d
commit eaf1a4baec
No known key found for this signature in database
GPG Key ID: F50EF5741D33E076
4 changed files with 56 additions and 21 deletions

View File

@ -1,12 +1,17 @@
from django.shortcuts import HttpResponseRedirect
from django.urls import reverse_lazy
from django.urls import reverse_lazy, Resolver404
from django.db import connection
from django.shortcuts import redirect
from django.conf.urls import include, url
import logging
import time
import operator
from rest_framework.authtoken.models import Token
from allauth_2fa.middleware import BaseRequire2FAMiddleware
from InvenTree.urls import frontendpatterns
logger = logging.getLogger("inventree")
@ -146,3 +151,16 @@ class QueryCountMiddleware(object):
print(x[0], ':', x[1])
return response
url_matcher = url('', include(frontendpatterns))
class Check2FAMiddleware(BaseRequire2FAMiddleware):
def require_2fa(self, request):
# Superusers are require to have 2FA.
try:
if url_matcher.resolve(request.path[1:]):
return True
except Resolver404:
pass
return False

View File

@ -304,7 +304,8 @@ MIDDLEWARE = CONFIG.get('middleware', [
'allauth_2fa.middleware.AllauthTwoFactorMiddleware', # Flow control for allauth
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'InvenTree.middleware.AuthRequiredMiddleware'
'InvenTree.middleware.AuthRequiredMiddleware',
'InvenTree.middleware.Check2FAMiddleware', # Check if the user should be forced to use MFA
])
# Error reporting middleware

View File

@ -37,7 +37,7 @@ from rest_framework.documentation import include_docs_urls
from .views import auth_request
from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView, CustomEmailView, CustomConnectionsView, CustomPasswordResetFromKeyView
from .views import SettingsView, EditUserView, SetPasswordView, CustomEmailView, CustomConnectionsView, CustomPasswordResetFromKeyView, CustomTwoFactorAuthenticate
from .views import CurrencyRefreshView
from .views import AppearanceSelectView, SettingCategorySelectView
from .views import DynamicJsView
@ -122,15 +122,29 @@ translated_javascript_urls = [
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/translated/table_filters.js'), name='table_filters.js'),
]
urlpatterns = [
url(r'^part/', include(part_urls)),
url(r'^manufacturer-part/', include(manufacturer_part_urls)),
url(r'^supplier-part/', include(supplier_part_urls)),
backendpatterns = [
# "Dynamic" javascript files which are rendered using InvenTree templating.
url(r'^js/dynamic/', include(dynamic_javascript_urls)),
url(r'^js/i18n/', include(translated_javascript_urls)),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^auth/?', auth_request),
url(r'^admin/error_log/', include('error_report.urls')),
url(r'^admin/shell/', include('django_admin_shell.urls')),
url(r'^admin/', admin.site.urls, name='inventree-admin'),
url(r'^api/', include(apipatterns)),
url(r'^api-doc/', include_docs_urls(title='InvenTree API')),
url(r'^markdownx/', include('markdownx.urls')),
]
frontendpatterns = [
url(r'^part/', include(part_urls)),
url(r'^manufacturer-part/', include(manufacturer_part_urls)),
url(r'^supplier-part/', include(supplier_part_urls)),
url(r'^common/', include(common_urls)),
url(r'^stock/', include(stock_urls)),
@ -140,37 +154,30 @@ urlpatterns = [
url(r'^build/', include(build_urls)),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^settings/', include(settings_urls)),
url(r'^edit-user/', EditUserView.as_view(), name='edit-user'),
url(r'^set-password/', SetPasswordView.as_view(), name='set-password'),
url(r'^admin/error_log/', include('error_report.urls')),
url(r'^admin/shell/', include('django_admin_shell.urls')),
url(r'^admin/', admin.site.urls, name='inventree-admin'),
url(r'^index/', IndexView.as_view(), name='index'),
url(r'^search/', SearchView.as_view(), name='search'),
url(r'^stats/', DatabaseStatsView.as_view(), name='stats'),
url(r'^auth/?', auth_request),
url(r'^api/', include(apipatterns)),
url(r'^api-doc/', include_docs_urls(title='InvenTree API')),
url(r'^markdownx/', include('markdownx.urls')),
# Single Sign On / allauth
# overrides of urlpatterns
url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),
url(r'^accounts/social/connections/', CustomConnectionsView.as_view(), name='socialaccount_connections'),
url(r"^accounts/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$", CustomPasswordResetFromKeyView.as_view(), name="account_reset_password_from_key"),
url(r"^accounts/two-factor-authenticate/?$", CustomTwoFactorAuthenticate.as_view(), name="two-factor-authenticate"),
url(r'^accounts/', include('allauth_2fa.urls')), # MFA support
url(r'^accounts/', include('allauth.urls')), # included urlpatterns
]
urlpatterns = [
url('', include(frontendpatterns)),
url('', include(backendpatterns)),
]
# Server running in "DEBUG" mode?
if settings.DEBUG:
# Static file access

View File

@ -29,6 +29,7 @@ from allauth.socialaccount.forms import DisconnectForm
from allauth.account.models import EmailAddress
from allauth.account.views import EmailView, PasswordResetFromKeyView
from allauth.socialaccount.views import ConnectionsView
from allauth_2fa.views import TwoFactorAuthenticate
from common.settings import currency_code_default, currency_codes
@ -857,6 +858,14 @@ class CustomPasswordResetFromKeyView(PasswordResetFromKeyView):
success_url = reverse_lazy("account_login")
class CustomTwoFactorAuthenticate(TwoFactorAuthenticate):
def dispatch(self, request, *args, **kwargs):
if 'allauth_2fa_user_id' not in request.session and 'otp_token' not in request.POST:
return redirect('account_login')
if hasattr(request.user, 'id'):
request.session['allauth_2fa_user_id'] = request.user.id
return super(FormView, self).dispatch(request, *args, **kwargs)
class CurrencyRefreshView(RedirectView):
"""
POST endpoint to refresh / update exchange rates