mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
Override 2FA token removal form (#3240)
- Requires user to input valid token to remove 2FA for their account Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
parent
b9c6cd70d4
commit
f9aa5a60fd
@ -17,6 +17,7 @@ from allauth.account.forms import SignupForm, set_form_field_order
|
|||||||
from allauth.exceptions import ImmediateHttpResponse
|
from allauth.exceptions import ImmediateHttpResponse
|
||||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||||
from allauth_2fa.adapter import OTPAdapter
|
from allauth_2fa.adapter import OTPAdapter
|
||||||
|
from allauth_2fa.forms import TOTPDeviceRemoveForm
|
||||||
from allauth_2fa.utils import user_has_valid_totp_device
|
from allauth_2fa.utils import user_has_valid_totp_device
|
||||||
from crispy_forms.bootstrap import (AppendedText, Div, PrependedAppendedText,
|
from crispy_forms.bootstrap import (AppendedText, Div, PrependedAppendedText,
|
||||||
PrependedText, StrictButton)
|
PrependedText, StrictButton)
|
||||||
@ -325,3 +326,36 @@ class CustomSocialAccountAdapter(RegistratonMixin, DefaultSocialAccountAdapter):
|
|||||||
|
|
||||||
# Otherwise defer to the original allauth adapter.
|
# Otherwise defer to the original allauth adapter.
|
||||||
return super().login(request, user)
|
return super().login(request, user)
|
||||||
|
|
||||||
|
|
||||||
|
# Temporary fix for django-allauth-2fa # TODO remove
|
||||||
|
# See https://github.com/inventree/InvenTree/security/advisories/GHSA-8j76-mm54-52xq
|
||||||
|
|
||||||
|
class CustomTOTPDeviceRemoveForm(TOTPDeviceRemoveForm):
|
||||||
|
"""Custom Form to ensure a token is provided before removing MFA"""
|
||||||
|
# User must input a valid token so 2FA can be removed
|
||||||
|
token = forms.CharField(
|
||||||
|
label=_('Token'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, user, **kwargs):
|
||||||
|
"""Add token field."""
|
||||||
|
super().__init__(user, **kwargs)
|
||||||
|
self.fields['token'].widget.attrs.update(
|
||||||
|
{
|
||||||
|
'autofocus': 'autofocus',
|
||||||
|
'autocomplete': 'off',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean_token(self):
|
||||||
|
"""Ensure at least one valid token is provided."""
|
||||||
|
# Ensure that the user has provided a valid token
|
||||||
|
token = self.cleaned_data.get('token')
|
||||||
|
|
||||||
|
# Verify that the user has provided a valid token
|
||||||
|
for device in self.user.totpdevice_set.filter(confirmed=True):
|
||||||
|
if device.verify_token(token):
|
||||||
|
return token
|
||||||
|
|
||||||
|
raise forms.ValidationError(_("The entered token is not valid"))
|
||||||
|
@ -36,9 +36,10 @@ from .views import (AppearanceSelectView, CurrencyRefreshView,
|
|||||||
CustomConnectionsView, CustomEmailView,
|
CustomConnectionsView, CustomEmailView,
|
||||||
CustomPasswordResetFromKeyView,
|
CustomPasswordResetFromKeyView,
|
||||||
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
||||||
DatabaseStatsView, DynamicJsView, EditUserView, IndexView,
|
CustomTwoFactorRemove, DatabaseStatsView, DynamicJsView,
|
||||||
NotificationsView, SearchView, SetPasswordView,
|
EditUserView, IndexView, NotificationsView, SearchView,
|
||||||
SettingCategorySelectView, SettingsView, auth_request)
|
SetPasswordView, SettingCategorySelectView, SettingsView,
|
||||||
|
auth_request)
|
||||||
|
|
||||||
admin.site.site_header = "InvenTree Admin"
|
admin.site.site_header = "InvenTree Admin"
|
||||||
|
|
||||||
@ -169,6 +170,11 @@ frontendpatterns = [
|
|||||||
re_path(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),
|
re_path(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),
|
||||||
re_path(r'^accounts/social/connections/', CustomConnectionsView.as_view(), name='socialaccount_connections'),
|
re_path(r'^accounts/social/connections/', CustomConnectionsView.as_view(), name='socialaccount_connections'),
|
||||||
re_path(r"^accounts/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$", CustomPasswordResetFromKeyView.as_view(), name="account_reset_password_from_key"),
|
re_path(r"^accounts/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/$", CustomPasswordResetFromKeyView.as_view(), name="account_reset_password_from_key"),
|
||||||
|
|
||||||
|
# Temporary fix for django-allauth-2fa # TODO remove
|
||||||
|
# See https://github.com/inventree/InvenTree/security/advisories/GHSA-8j76-mm54-52xq
|
||||||
|
re_path(r'^accounts/two_factor/remove/?$', CustomTwoFactorRemove.as_view(), name='two-factor-remove'),
|
||||||
|
|
||||||
re_path(r'^accounts/', include('allauth_2fa.urls')), # MFA support
|
re_path(r'^accounts/', include('allauth_2fa.urls')), # MFA support
|
||||||
re_path(r'^accounts/', include('allauth.urls')), # included urlpatterns
|
re_path(r'^accounts/', include('allauth.urls')), # included urlpatterns
|
||||||
]
|
]
|
||||||
|
@ -27,6 +27,7 @@ from allauth.account.models import EmailAddress
|
|||||||
from allauth.account.views import EmailView, PasswordResetFromKeyView
|
from allauth.account.views import EmailView, PasswordResetFromKeyView
|
||||||
from allauth.socialaccount.forms import DisconnectForm
|
from allauth.socialaccount.forms import DisconnectForm
|
||||||
from allauth.socialaccount.views import ConnectionsView
|
from allauth.socialaccount.views import ConnectionsView
|
||||||
|
from allauth_2fa.views import TwoFactorRemove
|
||||||
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
|
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
|
||||||
from user_sessions.views import SessionDeleteOtherView, SessionDeleteView
|
from user_sessions.views import SessionDeleteOtherView, SessionDeleteView
|
||||||
|
|
||||||
@ -35,8 +36,8 @@ from common.settings import currency_code_default, currency_codes
|
|||||||
from part.models import PartCategory
|
from part.models import PartCategory
|
||||||
from users.models import RuleSet, check_user_role
|
from users.models import RuleSet, check_user_role
|
||||||
|
|
||||||
from .forms import (DeleteForm, EditUserForm, SetPasswordForm,
|
from .forms import (CustomTOTPDeviceRemoveForm, DeleteForm, EditUserForm,
|
||||||
SettingCategorySelectForm)
|
SetPasswordForm, SettingCategorySelectForm)
|
||||||
from .helpers import str2bool
|
from .helpers import str2bool
|
||||||
|
|
||||||
|
|
||||||
@ -880,3 +881,12 @@ class NotificationsView(TemplateView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
template_name = "InvenTree/notifications/notifications.html"
|
template_name = "InvenTree/notifications/notifications.html"
|
||||||
|
|
||||||
|
|
||||||
|
# Temporary fix for django-allauth-2fa # TODO remove
|
||||||
|
# See https://github.com/inventree/InvenTree/security/advisories/GHSA-8j76-mm54-52xq
|
||||||
|
|
||||||
|
class CustomTwoFactorRemove(TwoFactorRemove):
|
||||||
|
"""Use custom form."""
|
||||||
|
form_class = CustomTOTPDeviceRemoveForm
|
||||||
|
success_url = reverse_lazy("settings")
|
||||||
|
@ -7,8 +7,8 @@ coverage==5.3 # Unit test coverage
|
|||||||
coveralls==2.1.2 # Coveralls linking (for Travis)
|
coveralls==2.1.2 # Coveralls linking (for Travis)
|
||||||
cryptography==3.4.8 # Cryptography support
|
cryptography==3.4.8 # Cryptography support
|
||||||
django-admin-shell==0.1.2 # Python shell for the admin interface
|
django-admin-shell==0.1.2 # Python shell for the admin interface
|
||||||
django-allauth==0.45.0 # SSO for external providers via OpenID
|
django-allauth==0.48.0 # SSO for external providers via OpenID
|
||||||
django-allauth-2fa==0.8 # MFA / 2FA
|
django-allauth-2fa==0.9 # MFA / 2FA # IMPORTANT: Do only change after reviewing GHSA-8j76-mm54-52xq
|
||||||
django-cleanup==5.1.0 # Manage deletion of old / unused uploaded files
|
django-cleanup==5.1.0 # Manage deletion of old / unused uploaded files
|
||||||
django-cors-headers==3.2.0 # CORS headers extension for DRF
|
django-cors-headers==3.2.0 # CORS headers extension for DRF
|
||||||
django-crispy-forms==1.11.2 # Form helpers
|
django-crispy-forms==1.11.2 # Form helpers
|
||||||
|
Loading…
x
Reference in New Issue
Block a user