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
from urllib.parse import urlencode
from django import forms
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.urls import reverse
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.forms import TOTPDeviceForm
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 rest_framework import serializers
@ -31,115 +28,6 @@ from InvenTree.exceptions import log_error
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
class CustomLoginForm(LoginForm):
"""Custom login form to override default allauth behaviour."""
@ -214,7 +102,10 @@ class CustomTOTPDeviceForm(TOTPDeviceForm):
def registration_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:
return True
else:
@ -316,10 +207,8 @@ class CustomAccountAdapter(
def get_email_confirmation_url(self, request, emailconfirmation):
"""Construct the email confirmation url."""
from InvenTree.helpers_model import construct_absolute_url
url = super().get_email_confirmation_url(request, emailconfirmation)
url = construct_absolute_url(url)
url = InvenTree.helpers_model.construct_absolute_url(url)
return url

View File

@ -592,7 +592,7 @@ REST_AUTH = {
OLD_PASSWORD_FIELD_ENABLED = True
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'InvenTree.forms.CustomRegisterSerializer'
'REGISTER_SERIALIZER': 'InvenTree.auth_overrides.CustomRegisterSerializer'
}
# JWT settings - rest_framework_simplejwt
@ -1312,8 +1312,8 @@ REMOVE_SUCCESS_URL = 'settings'
# override forms / adapters
ACCOUNT_FORMS = {
'login': 'InvenTree.forms.CustomLoginForm',
'signup': 'InvenTree.forms.CustomSignupForm',
'login': 'InvenTree.auth_overrides.CustomLoginForm',
'signup': 'InvenTree.auth_overrides.CustomSignupForm',
'add_email': 'allauth.account.forms.AddEmailForm',
'change_password': 'allauth.account.forms.ChangePasswordForm',
'set_password': 'allauth.account.forms.SetPasswordForm',
@ -1321,12 +1321,12 @@ ACCOUNT_FORMS = {
'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm',
'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)
MFA_ENABLED = get_boolean_setting('INVENTREE_MFA_ENABLED', 'mfa_enabled', True)
SOCIALACCOUNT_ADAPTER = 'InvenTree.forms.CustomSocialAccountAdapter'
ACCOUNT_ADAPTER = 'InvenTree.forms.CustomAccountAdapter'
SOCIALACCOUNT_ADAPTER = 'InvenTree.auth_overrides.CustomSocialAccountAdapter'
ACCOUNT_ADAPTER = 'InvenTree.auth_overrides.CustomAccountAdapter'
# Markdownify configuration
# Ref: https://django-markdownify.readthedocs.io/en/latest/settings.html

View File

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

View File

@ -69,12 +69,12 @@ def provider_display_name(provider):
return provider.name
def login_enabled() -> bool:
def sso_login_enabled() -> bool:
"""Return True if SSO login is enabled."""
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 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 InvenTree import sso
from InvenTree.forms import RegistratonMixin
from InvenTree.auth_overrides import RegistratonMixin
class Dummy: