2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 11:36:44 +00:00

Refactor auth adaptations into dedicated file (#8687)

* Update docker.yaml (#278)

* reset

* rename functions to better reflect function

* move authentication behaviour overrides to explicit file

* fix import order

* fix import path
This commit is contained in:
Matthias Mair 2024-12-17 12:20:21 +01:00 committed by GitHub
parent acb756eacc
commit 8fcb3c2506
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 18 additions and 129 deletions

View File

@ -1,11 +1,11 @@
"""Helper forms which subclass Django forms to provide additional functionality.""" """Overrides for allauth and adjacent packages to enforce InvenTree specific auth settings and restirctions."""
import logging import logging
from urllib.parse import urlencode from urllib.parse import urlencode
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -17,9 +17,6 @@ from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth_2fa.adapter import OTPAdapter from allauth_2fa.adapter import OTPAdapter
from allauth_2fa.forms import TOTPDeviceForm from allauth_2fa.forms import TOTPDeviceForm
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, PrependedAppendedText, PrependedText
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Field, Layout
from dj_rest_auth.registration.serializers import RegisterSerializer from dj_rest_auth.registration.serializers import RegisterSerializer
from rest_framework import serializers from rest_framework import serializers
@ -31,115 +28,6 @@ from InvenTree.exceptions import log_error
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
class HelperForm(forms.ModelForm):
"""Provides simple integration of crispy_forms extension."""
# Custom field decorations can be specified here, per form class
field_prefix = {}
field_suffix = {}
field_placeholder = {}
def __init__(self, *args, **kwargs):
"""Setup layout."""
super(forms.ModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_show_errors = True
"""
Create a default 'layout' for this form.
Ref: https://django-crispy-forms.readthedocs.io/en/latest/layouts.html
This is required to do fancy things later (like adding PrependedText, etc).
Simply create a 'blank' layout for each available field.
"""
self.rebuild_layout()
def rebuild_layout(self):
"""Build crispy layout out of current fields."""
layouts = []
for field in self.fields:
prefix = self.field_prefix.get(field, None)
suffix = self.field_suffix.get(field, None)
placeholder = self.field_placeholder.get(field, '')
# Look for font-awesome icons
if prefix and prefix.startswith('fa-'):
prefix = f"<i class='fas {prefix}'/>"
if suffix and suffix.startswith('fa-'):
suffix = f"<i class='fas {suffix}'/>"
if prefix and suffix:
layouts.append(
Field(
PrependedAppendedText(
field,
prepended_text=prefix,
appended_text=suffix,
placeholder=placeholder,
)
)
)
elif prefix:
layouts.append(
Field(PrependedText(field, prefix, placeholder=placeholder))
)
elif suffix:
layouts.append(
Field(AppendedText(field, suffix, placeholder=placeholder))
)
else:
layouts.append(Field(field, placeholder=placeholder))
self.helper.layout = Layout(*layouts)
class SetPasswordForm(HelperForm):
"""Form for setting user password."""
class Meta:
"""Metaclass options."""
model = User
fields = ['enter_password', 'confirm_password', 'old_password']
enter_password = forms.CharField(
max_length=100,
min_length=8,
required=True,
initial='',
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
label=_('Enter password'),
help_text=_('Enter new password'),
)
confirm_password = forms.CharField(
max_length=100,
min_length=8,
required=True,
initial='',
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
label=_('Confirm password'),
help_text=_('Confirm new password'),
)
old_password = forms.CharField(
label=_('Old password'),
strip=False,
required=False,
widget=forms.PasswordInput(
attrs={'autocomplete': 'current-password', 'autofocus': True}
),
)
# override allauth # override allauth
class CustomLoginForm(LoginForm): class CustomLoginForm(LoginForm):
"""Custom login form to override default allauth behaviour.""" """Custom login form to override default allauth behaviour."""
@ -214,7 +102,10 @@ class CustomTOTPDeviceForm(TOTPDeviceForm):
def registration_enabled(): def registration_enabled():
"""Determine whether user registration is enabled.""" """Determine whether user registration is enabled."""
if get_global_setting('LOGIN_ENABLE_REG') or InvenTree.sso.registration_enabled(): if (
get_global_setting('LOGIN_ENABLE_REG')
or InvenTree.sso.sso_registration_enabled()
):
if settings.EMAIL_HOST: if settings.EMAIL_HOST:
return True return True
else: else:
@ -316,10 +207,8 @@ class CustomAccountAdapter(
def get_email_confirmation_url(self, request, emailconfirmation): def get_email_confirmation_url(self, request, emailconfirmation):
"""Construct the email confirmation url.""" """Construct the email confirmation url."""
from InvenTree.helpers_model import construct_absolute_url
url = super().get_email_confirmation_url(request, emailconfirmation) url = super().get_email_confirmation_url(request, emailconfirmation)
url = construct_absolute_url(url) url = InvenTree.helpers_model.construct_absolute_url(url)
return url return url

View File

@ -592,7 +592,7 @@ REST_AUTH = {
OLD_PASSWORD_FIELD_ENABLED = True OLD_PASSWORD_FIELD_ENABLED = True
REST_AUTH_REGISTER_SERIALIZERS = { REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'InvenTree.forms.CustomRegisterSerializer' 'REGISTER_SERIALIZER': 'InvenTree.auth_overrides.CustomRegisterSerializer'
} }
# JWT settings - rest_framework_simplejwt # JWT settings - rest_framework_simplejwt
@ -1312,8 +1312,8 @@ REMOVE_SUCCESS_URL = 'settings'
# override forms / adapters # override forms / adapters
ACCOUNT_FORMS = { ACCOUNT_FORMS = {
'login': 'InvenTree.forms.CustomLoginForm', 'login': 'InvenTree.auth_overrides.CustomLoginForm',
'signup': 'InvenTree.forms.CustomSignupForm', 'signup': 'InvenTree.auth_overrides.CustomSignupForm',
'add_email': 'allauth.account.forms.AddEmailForm', 'add_email': 'allauth.account.forms.AddEmailForm',
'change_password': 'allauth.account.forms.ChangePasswordForm', 'change_password': 'allauth.account.forms.ChangePasswordForm',
'set_password': 'allauth.account.forms.SetPasswordForm', 'set_password': 'allauth.account.forms.SetPasswordForm',
@ -1321,12 +1321,12 @@ ACCOUNT_FORMS = {
'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm', 'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm',
'disconnect': 'allauth.socialaccount.forms.DisconnectForm', 'disconnect': 'allauth.socialaccount.forms.DisconnectForm',
} }
ALLAUTH_2FA_FORMS = {'setup': 'InvenTree.forms.CustomTOTPDeviceForm'} ALLAUTH_2FA_FORMS = {'setup': 'InvenTree.auth_overrides.CustomTOTPDeviceForm'}
# Determine if multi-factor authentication is enabled for this server (default = True) # Determine if multi-factor authentication is enabled for this server (default = True)
MFA_ENABLED = get_boolean_setting('INVENTREE_MFA_ENABLED', 'mfa_enabled', True) MFA_ENABLED = get_boolean_setting('INVENTREE_MFA_ENABLED', 'mfa_enabled', True)
SOCIALACCOUNT_ADAPTER = 'InvenTree.forms.CustomSocialAccountAdapter' SOCIALACCOUNT_ADAPTER = 'InvenTree.auth_overrides.CustomSocialAccountAdapter'
ACCOUNT_ADAPTER = 'InvenTree.forms.CustomAccountAdapter' ACCOUNT_ADAPTER = 'InvenTree.auth_overrides.CustomAccountAdapter'
# Markdownify configuration # Markdownify configuration
# Ref: https://django-markdownify.readthedocs.io/en/latest/settings.html # Ref: https://django-markdownify.readthedocs.io/en/latest/settings.html

View File

@ -198,8 +198,8 @@ class SocialProviderListView(ListAPI):
provider_list.append(provider_data) provider_list.append(provider_data)
data = { data = {
'sso_enabled': InvenTree.sso.login_enabled(), 'sso_enabled': InvenTree.sso.sso_login_enabled(),
'sso_registration': InvenTree.sso.registration_enabled(), 'sso_registration': InvenTree.sso.sso_registration_enabled(),
'mfa_required': settings.MFA_ENABLED 'mfa_required': settings.MFA_ENABLED
and get_global_setting('LOGIN_ENFORCE_MFA'), and get_global_setting('LOGIN_ENFORCE_MFA'),
'mfa_enabled': settings.MFA_ENABLED, 'mfa_enabled': settings.MFA_ENABLED,

View File

@ -69,12 +69,12 @@ def provider_display_name(provider):
return provider.name return provider.name
def login_enabled() -> bool: def sso_login_enabled() -> bool:
"""Return True if SSO login is enabled.""" """Return True if SSO login is enabled."""
return str2bool(get_global_setting('LOGIN_ENABLE_SSO')) return str2bool(get_global_setting('LOGIN_ENABLE_SSO'))
def registration_enabled() -> bool: def sso_registration_enabled() -> bool:
"""Return True if SSO registration is enabled.""" """Return True if SSO registration is enabled."""
return str2bool(get_global_setting('LOGIN_ENABLE_SSO_REG')) return str2bool(get_global_setting('LOGIN_ENABLE_SSO_REG'))

View File

@ -8,7 +8,7 @@ from allauth.socialaccount.models import SocialAccount, SocialLogin
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
from InvenTree import sso from InvenTree import sso
from InvenTree.forms import RegistratonMixin from InvenTree.auth_overrides import RegistratonMixin
class Dummy: class Dummy: