Skip to content

Commit

Permalink
Override 2FA token removal form (#3240)
Browse files Browse the repository at this point in the history
- Requires user to input valid token to remove 2FA for their account

Co-authored-by: Matthias Mair <code@mjmair.com>
  • Loading branch information
SchrodingersGat and matmair committed Jun 23, 2022
1 parent b9c6cd7 commit f9aa5a6
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 7 deletions.
34 changes: 34 additions & 0 deletions InvenTree/InvenTree/forms.py
Expand Up @@ -17,6 +17,7 @@
from allauth.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth_2fa.adapter import OTPAdapter
from allauth_2fa.forms import TOTPDeviceRemoveForm
from allauth_2fa.utils import user_has_valid_totp_device
from crispy_forms.bootstrap import (AppendedText, Div, PrependedAppendedText,
PrependedText, StrictButton)
Expand Down Expand Up @@ -325,3 +326,36 @@ def login(self, request, user):

# Otherwise defer to the original allauth adapter.
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"))
12 changes: 9 additions & 3 deletions InvenTree/InvenTree/urls.py
Expand Up @@ -36,9 +36,10 @@
CustomConnectionsView, CustomEmailView,
CustomPasswordResetFromKeyView,
CustomSessionDeleteOtherView, CustomSessionDeleteView,
DatabaseStatsView, DynamicJsView, EditUserView, IndexView,
NotificationsView, SearchView, SetPasswordView,
SettingCategorySelectView, SettingsView, auth_request)
CustomTwoFactorRemove, DatabaseStatsView, DynamicJsView,
EditUserView, IndexView, NotificationsView, SearchView,
SetPasswordView, SettingCategorySelectView, SettingsView,
auth_request)

admin.site.site_header = "InvenTree Admin"

Expand Down Expand Up @@ -169,6 +170,11 @@
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/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.urls')), # included urlpatterns
]
Expand Down
14 changes: 12 additions & 2 deletions InvenTree/InvenTree/views.py
Expand Up @@ -27,6 +27,7 @@
from allauth.account.views import EmailView, PasswordResetFromKeyView
from allauth.socialaccount.forms import DisconnectForm
from allauth.socialaccount.views import ConnectionsView
from allauth_2fa.views import TwoFactorRemove
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
from user_sessions.views import SessionDeleteOtherView, SessionDeleteView

Expand All @@ -35,8 +36,8 @@
from part.models import PartCategory
from users.models import RuleSet, check_user_role

from .forms import (DeleteForm, EditUserForm, SetPasswordForm,
SettingCategorySelectForm)
from .forms import (CustomTOTPDeviceRemoveForm, DeleteForm, EditUserForm,
SetPasswordForm, SettingCategorySelectForm)
from .helpers import str2bool


Expand Down Expand Up @@ -880,3 +881,12 @@ class NotificationsView(TemplateView):
"""

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")
4 changes: 2 additions & 2 deletions requirements.txt
Expand Up @@ -7,8 +7,8 @@ coverage==5.3 # Unit test coverage
coveralls==2.1.2 # Coveralls linking (for Travis)
cryptography==3.4.8 # Cryptography support
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-2fa==0.8 # MFA / 2FA
django-allauth==0.48.0 # SSO for external providers via OpenID
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-cors-headers==3.2.0 # CORS headers extension for DRF
django-crispy-forms==1.11.2 # Form helpers
Expand Down

0 comments on commit f9aa5a6

Please sign in to comment.