mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
enforce mfa on all frontend pages
This commit is contained in:
parent
527bc4381d
commit
eaf1a4baec
@ -1,12 +1,17 @@
|
|||||||
from django.shortcuts import HttpResponseRedirect
|
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.db import connection
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from django.conf.urls import include, url
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
|
from allauth_2fa.middleware import BaseRequire2FAMiddleware
|
||||||
|
|
||||||
|
from InvenTree.urls import frontendpatterns
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("inventree")
|
logger = logging.getLogger("inventree")
|
||||||
|
|
||||||
@ -146,3 +151,16 @@ class QueryCountMiddleware(object):
|
|||||||
print(x[0], ':', x[1])
|
print(x[0], ':', x[1])
|
||||||
|
|
||||||
return response
|
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
|
||||||
|
@ -304,7 +304,8 @@ MIDDLEWARE = CONFIG.get('middleware', [
|
|||||||
'allauth_2fa.middleware.AllauthTwoFactorMiddleware', # Flow control for allauth
|
'allauth_2fa.middleware.AllauthTwoFactorMiddleware', # Flow control for allauth
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'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
|
# Error reporting middleware
|
||||||
|
@ -37,7 +37,7 @@ from rest_framework.documentation import include_docs_urls
|
|||||||
|
|
||||||
from .views import auth_request
|
from .views import auth_request
|
||||||
from .views import IndexView, SearchView, DatabaseStatsView
|
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 CurrencyRefreshView
|
||||||
from .views import AppearanceSelectView, SettingCategorySelectView
|
from .views import AppearanceSelectView, SettingCategorySelectView
|
||||||
from .views import DynamicJsView
|
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'),
|
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/translated/table_filters.js'), name='table_filters.js'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
backendpatterns = [
|
||||||
url(r'^part/', include(part_urls)),
|
|
||||||
url(r'^manufacturer-part/', include(manufacturer_part_urls)),
|
|
||||||
url(r'^supplier-part/', include(supplier_part_urls)),
|
|
||||||
|
|
||||||
# "Dynamic" javascript files which are rendered using InvenTree templating.
|
# "Dynamic" javascript files which are rendered using InvenTree templating.
|
||||||
url(r'^js/dynamic/', include(dynamic_javascript_urls)),
|
url(r'^js/dynamic/', include(dynamic_javascript_urls)),
|
||||||
url(r'^js/i18n/', include(translated_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'^common/', include(common_urls)),
|
||||||
|
|
||||||
url(r'^stock/', include(stock_urls)),
|
url(r'^stock/', include(stock_urls)),
|
||||||
@ -140,37 +154,30 @@ urlpatterns = [
|
|||||||
|
|
||||||
url(r'^build/', include(build_urls)),
|
url(r'^build/', include(build_urls)),
|
||||||
|
|
||||||
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
|
|
||||||
|
|
||||||
url(r'^settings/', include(settings_urls)),
|
url(r'^settings/', include(settings_urls)),
|
||||||
|
|
||||||
url(r'^edit-user/', EditUserView.as_view(), name='edit-user'),
|
url(r'^edit-user/', EditUserView.as_view(), name='edit-user'),
|
||||||
url(r'^set-password/', SetPasswordView.as_view(), name='set-password'),
|
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'^index/', IndexView.as_view(), name='index'),
|
||||||
url(r'^search/', SearchView.as_view(), name='search'),
|
url(r'^search/', SearchView.as_view(), name='search'),
|
||||||
url(r'^stats/', DatabaseStatsView.as_view(), name='stats'),
|
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
|
# Single Sign On / allauth
|
||||||
# overrides of urlpatterns
|
# overrides of urlpatterns
|
||||||
url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),
|
url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),
|
||||||
url(r'^accounts/social/connections/', CustomConnectionsView.as_view(), name='socialaccount_connections'),
|
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/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_2fa.urls')), # MFA support
|
||||||
url(r'^accounts/', include('allauth.urls')), # included urlpatterns
|
url(r'^accounts/', include('allauth.urls')), # included urlpatterns
|
||||||
]
|
]
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url('', include(frontendpatterns)),
|
||||||
|
url('', include(backendpatterns)),
|
||||||
|
]
|
||||||
|
|
||||||
# Server running in "DEBUG" mode?
|
# Server running in "DEBUG" mode?
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
# Static file access
|
# Static file access
|
||||||
|
@ -29,6 +29,7 @@ from allauth.socialaccount.forms import DisconnectForm
|
|||||||
from allauth.account.models import EmailAddress
|
from allauth.account.models import EmailAddress
|
||||||
from allauth.account.views import EmailView, PasswordResetFromKeyView
|
from allauth.account.views import EmailView, PasswordResetFromKeyView
|
||||||
from allauth.socialaccount.views import ConnectionsView
|
from allauth.socialaccount.views import ConnectionsView
|
||||||
|
from allauth_2fa.views import TwoFactorAuthenticate
|
||||||
|
|
||||||
from common.settings import currency_code_default, currency_codes
|
from common.settings import currency_code_default, currency_codes
|
||||||
|
|
||||||
@ -857,6 +858,14 @@ class CustomPasswordResetFromKeyView(PasswordResetFromKeyView):
|
|||||||
success_url = reverse_lazy("account_login")
|
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):
|
class CurrencyRefreshView(RedirectView):
|
||||||
"""
|
"""
|
||||||
POST endpoint to refresh / update exchange rates
|
POST endpoint to refresh / update exchange rates
|
||||||
|
Loading…
Reference in New Issue
Block a user